@@ -252,7 +252,9 @@ namespace ts {
252
252
const gutterSeparator = " " ;
253
253
const resetEscapeSequence = "\u001b[0m" ;
254
254
const ellipsis = "..." ;
255
- function getCategoryFormat ( category : DiagnosticCategory ) : string {
255
+ const halfIndent = " " ;
256
+ const indent = " " ;
257
+ function getCategoryFormat ( category : DiagnosticCategory ) : ForegroundColorEscapeSequences {
256
258
switch ( category ) {
257
259
case DiagnosticCategory . Error : return ForegroundColorEscapeSequences . Red ;
258
260
case DiagnosticCategory . Warning : return ForegroundColorEscapeSequences . Yellow ;
@@ -273,68 +275,79 @@ namespace ts {
273
275
return s ;
274
276
}
275
277
278
+ function formatCodeSpan ( file : SourceFile , start : number , length : number , indent : string , squiggleColor : ForegroundColorEscapeSequences , host : FormatDiagnosticsHost ) {
279
+ const { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ) ;
280
+ const { line : lastLine , character : lastLineChar } = getLineAndCharacterOfPosition ( file , start + length ) ;
281
+ const lastLineInFile = getLineAndCharacterOfPosition ( file , file . text . length ) . line ;
282
+
283
+ const hasMoreThanFiveLines = ( lastLine - firstLine ) >= 4 ;
284
+ let gutterWidth = ( lastLine + 1 + "" ) . length ;
285
+ if ( hasMoreThanFiveLines ) {
286
+ gutterWidth = Math . max ( ellipsis . length , gutterWidth ) ;
287
+ }
288
+
289
+ let context = "" ;
290
+ for ( let i = firstLine ; i <= lastLine ; i ++ ) {
291
+ context += host . getNewLine ( ) ;
292
+ // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
293
+ // so we'll skip ahead to the second-to-last line.
294
+ if ( hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1 ) {
295
+ context += indent + formatColorAndReset ( padLeft ( ellipsis , gutterWidth ) , gutterStyleSequence ) + gutterSeparator + host . getNewLine ( ) ;
296
+ i = lastLine - 1 ;
297
+ }
298
+
299
+ const lineStart = getPositionOfLineAndCharacter ( file , i , 0 ) ;
300
+ const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter ( file , i + 1 , 0 ) : file . text . length ;
301
+ let lineContent = file . text . slice ( lineStart , lineEnd ) ;
302
+ lineContent = lineContent . replace ( / \s + $ / g, "" ) ; // trim from end
303
+ lineContent = lineContent . replace ( "\t" , " " ) ; // convert tabs to single spaces
304
+
305
+ // Output the gutter and the actual contents of the line.
306
+ context += indent + formatColorAndReset ( padLeft ( i + 1 + "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
307
+ context += lineContent + host . getNewLine ( ) ;
308
+
309
+ // Output the gutter and the error span for the line using tildes.
310
+ context += indent + formatColorAndReset ( padLeft ( "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
311
+ context += squiggleColor ;
312
+ if ( i === firstLine ) {
313
+ // If we're on the last line, then limit it to the last character of the last line.
314
+ // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
315
+ const lastCharForLine = i === lastLine ? lastLineChar : undefined ;
316
+
317
+ context += lineContent . slice ( 0 , firstLineChar ) . replace ( / \S / g, " " ) ;
318
+ context += lineContent . slice ( firstLineChar , lastCharForLine ) . replace ( / ./ g, "~" ) ;
319
+ }
320
+ else if ( i === lastLine ) {
321
+ context += lineContent . slice ( 0 , lastLineChar ) . replace ( / ./ g, "~" ) ;
322
+ }
323
+ else {
324
+ // Squiggle the entire line.
325
+ context += lineContent . replace ( / ./ g, "~" ) ;
326
+ }
327
+ context += resetEscapeSequence ;
328
+ }
329
+ return context ;
330
+ }
331
+
332
+ function formatLocation ( file : SourceFile , start : number , host : FormatDiagnosticsHost ) {
333
+ const { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ) ; // TODO: GH#18217
334
+ const relativeFileName = host ? convertToRelativePath ( file . fileName , host . getCurrentDirectory ( ) , fileName => host . getCanonicalFileName ( fileName ) ) : file . fileName ;
335
+
336
+ let output = "" ;
337
+ output += formatColorAndReset ( relativeFileName , ForegroundColorEscapeSequences . Cyan ) ;
338
+ output += ":" ;
339
+ output += formatColorAndReset ( `${ firstLine + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
340
+ output += ":" ;
341
+ output += formatColorAndReset ( `${ firstLineChar + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
342
+ return output ;
343
+ }
344
+
276
345
export function formatDiagnosticsWithColorAndContext ( diagnostics : ReadonlyArray < Diagnostic > , host : FormatDiagnosticsHost ) : string {
277
346
let output = "" ;
278
347
for ( const diagnostic of diagnostics ) {
279
- let context = "" ;
280
348
if ( diagnostic . file ) {
281
- const { start, length, file } = diagnostic ;
282
- const { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ! ) ; // TODO: GH#18217
283
- const { line : lastLine , character : lastLineChar } = getLineAndCharacterOfPosition ( file , start ! + length ! ) ;
284
- const lastLineInFile = getLineAndCharacterOfPosition ( file , file . text . length ) . line ;
285
- const relativeFileName = host ? convertToRelativePath ( file . fileName , host . getCurrentDirectory ( ) , fileName => host . getCanonicalFileName ( fileName ) ) : file . fileName ;
286
-
287
- const hasMoreThanFiveLines = ( lastLine - firstLine ) >= 4 ;
288
- let gutterWidth = ( lastLine + 1 + "" ) . length ;
289
- if ( hasMoreThanFiveLines ) {
290
- gutterWidth = Math . max ( ellipsis . length , gutterWidth ) ;
291
- }
292
-
293
- for ( let i = firstLine ; i <= lastLine ; i ++ ) {
294
- context += host . getNewLine ( ) ;
295
- // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
296
- // so we'll skip ahead to the second-to-last line.
297
- if ( hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1 ) {
298
- context += formatColorAndReset ( padLeft ( ellipsis , gutterWidth ) , gutterStyleSequence ) + gutterSeparator + host . getNewLine ( ) ;
299
- i = lastLine - 1 ;
300
- }
301
-
302
- const lineStart = getPositionOfLineAndCharacter ( file , i , 0 ) ;
303
- const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter ( file , i + 1 , 0 ) : file . text . length ;
304
- let lineContent = file . text . slice ( lineStart , lineEnd ) ;
305
- lineContent = lineContent . replace ( / \s + $ / g, "" ) ; // trim from end
306
- lineContent = lineContent . replace ( "\t" , " " ) ; // convert tabs to single spaces
307
-
308
- // Output the gutter and the actual contents of the line.
309
- context += formatColorAndReset ( padLeft ( i + 1 + "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
310
- context += lineContent + host . getNewLine ( ) ;
311
-
312
- // Output the gutter and the error span for the line using tildes.
313
- context += formatColorAndReset ( padLeft ( "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
314
- context += ForegroundColorEscapeSequences . Red ;
315
- if ( i === firstLine ) {
316
- // If we're on the last line, then limit it to the last character of the last line.
317
- // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
318
- const lastCharForLine = i === lastLine ? lastLineChar : undefined ;
319
-
320
- context += lineContent . slice ( 0 , firstLineChar ) . replace ( / \S / g, " " ) ;
321
- context += lineContent . slice ( firstLineChar , lastCharForLine ) . replace ( / ./ g, "~" ) ;
322
- }
323
- else if ( i === lastLine ) {
324
- context += lineContent . slice ( 0 , lastLineChar ) . replace ( / ./ g, "~" ) ;
325
- }
326
- else {
327
- // Squiggle the entire line.
328
- context += lineContent . replace ( / ./ g, "~" ) ;
329
- }
330
- context += resetEscapeSequence ;
331
- }
332
-
333
- output += formatColorAndReset ( relativeFileName , ForegroundColorEscapeSequences . Cyan ) ;
334
- output += ":" ;
335
- output += formatColorAndReset ( `${ firstLine + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
336
- output += ":" ;
337
- output += formatColorAndReset ( `${ firstLineChar + 1 } ` , ForegroundColorEscapeSequences . Yellow ) ;
349
+ const { file, start } = diagnostic ;
350
+ output += formatLocation ( file , start ! , host ) ; // TODO: GH#18217
338
351
output += " - " ;
339
352
}
340
353
@@ -344,7 +357,19 @@ namespace ts {
344
357
345
358
if ( diagnostic . file ) {
346
359
output += host . getNewLine ( ) ;
347
- output += context ;
360
+ output += formatCodeSpan ( diagnostic . file , diagnostic . start ! , diagnostic . length ! , "" , getCategoryFormat ( diagnostic . category ) , host ) ; // TODO: GH#18217
361
+ if ( diagnostic . relatedInformation ) {
362
+ output += host . getNewLine ( ) ;
363
+ for ( const { file, start, length, messageText } of diagnostic . relatedInformation ) {
364
+ if ( file ) {
365
+ output += host . getNewLine ( ) ;
366
+ output += halfIndent + formatLocation ( file , start ! , host ) ; // TODO: GH#18217
367
+ output += formatCodeSpan ( file , start ! , length ! , indent , ForegroundColorEscapeSequences . Cyan , host ) ; // TODO: GH#18217
368
+ }
369
+ output += host . getNewLine ( ) ;
370
+ output += indent + flattenDiagnosticMessageText ( messageText , host . getNewLine ( ) ) ;
371
+ }
372
+ }
348
373
}
349
374
350
375
output += host . getNewLine ( ) ;
0 commit comments