@@ -306,31 +306,70 @@ export function consolidateIssues(issues: Issue[]): Issue[] {
306306 return merged ;
307307}
308308/**
309- * Format single issue into editor-like lines (array of strings)
309+ * Format a single Issue into editor-like lines (no duplicates).
310310 */
311311export function formatIssueAsEditorLines ( issue : Issue ) : string [ ] {
312312 const lines : string [ ] = [ ] ;
313- const typ = issue . type ? `${ issue . type } ` : 'Error' ;
314- lines . push ( `${ typ } error at ${ issue . path || issue . location || '<unknown>' } ` ) ;
315- lines . push ( issue . message || '(no message)' ) ;
316- if ( issue . details && issue . details . length ) {
317- for ( const d of issue . details ) lines . push ( d ) ;
313+
314+ const typ = ( issue . type ?? 'Error' ) . toString ( ) ;
315+ const target = issue . path || issue . location || '<unknown>' ;
316+ const header = `${ typ } error at ${ target } ` ;
317+
318+ // header
319+ lines . push ( header ) ;
320+
321+ // message
322+ const msg = ( issue . message ?? '' ) . toString ( ) . trim ( ) ;
323+ if ( msg ) lines . push ( msg ) ;
324+
325+ // details (only ones that are not identical to message)
326+ if ( Array . isArray ( issue . details ) && issue . details . length ) {
327+ for ( const d of issue . details ) {
328+ const dd = ( d ?? '' ) . toString ( ) . trim ( ) ;
329+ if ( ! dd ) continue ;
330+ if ( dd === msg ) continue ; // skip duplicate
331+ lines . push ( dd ) ;
332+ }
318333 }
319- if ( issue . raw && Array . isArray ( issue . raw ) ) {
334+
335+ // If raw is present, include only lines from raw that are not duplicates of already included lines
336+ if ( Array . isArray ( issue . raw ) && issue . raw . length ) {
320337 for ( const r of issue . raw ) {
321- const t = String ( r ) . trim ( ) ;
322- if ( t && ! lines . includes ( t ) ) lines . push ( t ) ;
338+ const rr = ( r ?? '' ) . toString ( ) . trim ( ) ;
339+ if ( ! rr ) continue ;
340+ // skip if it's identical to header, msg, any details or "Jump to line ..." (we will add jump ourselves)
341+ if ( rr === header ) continue ;
342+ if ( rr === msg ) continue ;
343+ if ( ( issue . details || [ ] ) . some ( d => ( d ?? '' ) . toString ( ) . trim ( ) === rr ) ) continue ;
344+ if ( / ^ J u m p t o l i n e \s + \d + / i. test ( rr ) ) continue ;
345+ lines . push ( rr ) ;
323346 }
324347 }
325- if ( typeof issue . line === 'number' ) lines . push ( `Jump to line ${ issue . line } ` ) ;
348+
349+ // Jump to line (prefer issue.line if present)
350+ const l = normalizeLine ( issue . line ) ;
351+ if ( l ) lines . push ( `Jump to line ${ l } ` ) ;
352+
326353 return lines ;
327354}
355+ function normalizeLine ( line ?: number | null ) : number | undefined {
356+ if ( typeof line === 'number' && Number . isFinite ( line ) && line > 0 ) return line ;
357+ return undefined ;
358+ }
328359/**
329- * Format array of issues to single editor-like string (for snippet)
360+ * Format entire Issue[] as a single editor-like text block.
361+ * Ensures issues are separated by a blank line.
330362 */
331363export function formatIssuesAsEditorText ( issues : Issue [ ] ) : string {
332- if ( ! issues || ! issues . length ) return '' ;
333- return issues . map ( i => formatIssueAsEditorLines ( i ) . join ( '\n' ) ) . join ( '\n\n' ) ;
364+ if ( ! Array . isArray ( issues ) || issues . length === 0 ) return '' ;
365+
366+ // We'll assume caller has already consolidated & sorted issues.
367+ const blocks : string [ ] = [ ] ;
368+ for ( const issue of issues ) {
369+ const lines = formatIssueAsEditorLines ( issue ) ;
370+ if ( lines . length ) blocks . push ( lines . join ( '\n' ) ) ;
371+ }
372+ return blocks . join ( '\n\n' ) ;
334373}
335374export function parsePrettyLinesToIssues ( prettyLines : string [ ] ) : Issue [ ] {
336375 const issues : Issue [ ] = [ ] ;
0 commit comments