@@ -6,6 +6,14 @@ namespace ts {
66 fileWatcher ?: FileWatcher ;
77 }
88
9+ let reportDiagnostic = reportDiagnosticSimply ;
10+
11+ function reportDiagnostics ( diagnostics : Diagnostic [ ] , host : CompilerHost ) : void {
12+ for ( let diagnostic of diagnostics ) {
13+ reportDiagnostic ( diagnostic , host ) ;
14+ }
15+ }
16+
917 /**
1018 * Checks to see if the locale is in the appropriate format,
1119 * and if it is, attempts to set the appropriate language.
@@ -81,16 +89,16 @@ namespace ts {
8189 return < string > diagnostic . messageText ;
8290 }
8391
84- function reportDiagnostic ( diagnostic : Diagnostic , host : CompilerHost ) {
92+ function reportDiagnosticSimply ( diagnostic : Diagnostic , host : CompilerHost ) : void {
8593 let output = "" ;
8694
8795 if ( diagnostic . file ) {
88- let loc = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
96+ const { line , character } = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
8997 const relativeFileName = host
9098 ? convertToRelativePath ( diagnostic . file . fileName , host . getCurrentDirectory ( ) , fileName => host . getCanonicalFileName ( fileName ) )
9199 : diagnostic . file . fileName ;
92100
93- output += `${ relativeFileName } (${ loc . line + 1 } ,${ loc . character + 1 } ): ` ;
101+ output += `${ diagnostic . file . fileName } (${ line + 1 } ,${ character + 1 } ): ` ;
94102 }
95103
96104 let category = DiagnosticCategory [ diagnostic . category ] . toLowerCase ( ) ;
@@ -99,10 +107,91 @@ namespace ts {
99107 sys . write ( output ) ;
100108 }
101109
102- function reportDiagnostics ( diagnostics : Diagnostic [ ] , host : CompilerHost ) {
103- for ( let i = 0 ; i < diagnostics . length ; i ++ ) {
104- reportDiagnostic ( diagnostics [ i ] , host ) ;
110+
111+ const redForegroundEscapeSequence = "\u001b[91m" ;
112+ const yellowForegroundEscapeSequence = "\u001b[93m" ;
113+ const blueForegroundEscapeSequence = "\u001b[93m" ;
114+ const gutterStyleSequence = "\u001b[100;30m" ;
115+ const gutterSeparator = " " ;
116+ const resetEscapeSequence = "\u001b[0m" ;
117+ const elipsis = "..." ;
118+ const categoryFormatMap : Map < string > = {
119+ [ DiagnosticCategory . Warning ] : yellowForegroundEscapeSequence ,
120+ [ DiagnosticCategory . Error ] : redForegroundEscapeSequence ,
121+ [ DiagnosticCategory . Message ] : blueForegroundEscapeSequence ,
122+ } ;
123+
124+ function formatAndReset ( text : string , formatStyle : string ) {
125+ return formatStyle + text + resetEscapeSequence ;
126+ }
127+
128+ function reportDiagnosticWithColorAndContext ( diagnostic : Diagnostic , host : CompilerHost ) : void {
129+ let output = "" ;
130+
131+ if ( diagnostic . file ) {
132+ let { start, length, file } = diagnostic ;
133+ let { line : firstLine , character : firstLineChar } = getLineAndCharacterOfPosition ( file , start ) ;
134+ let { line : lastLine , character : lastLineChar } = getLineAndCharacterOfPosition ( file , start + length ) ;
135+ const lastLineInFile = getLineAndCharacterOfPosition ( file , file . text . length ) . line ;
136+
137+ let hasMoreThanFiveLines = ( lastLine - firstLine ) >= 4 ;
138+ let gutterWidth = ( lastLine + 1 + "" ) . length ;
139+ if ( hasMoreThanFiveLines ) {
140+ gutterWidth = Math . max ( elipsis . length , gutterWidth ) ;
141+ }
142+
143+ output += sys . newLine ;
144+ for ( let i = firstLine ; i <= lastLine ; i ++ ) {
145+ // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines,
146+ // so we'll skip ahead to the second-to-last line.
147+ if ( hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1 ) {
148+ output += formatAndReset ( padLeft ( elipsis , gutterWidth ) , gutterStyleSequence ) + gutterSeparator + sys . newLine ;
149+ i = lastLine - 1 ;
150+ }
151+
152+ let lineStart = getPositionOfLineAndCharacter ( file , i , 0 ) ;
153+ let lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter ( file , i + 1 , 0 ) : file . text . length ;
154+ let lineContent = file . text . slice ( lineStart , lineEnd ) ;
155+ lineContent = lineContent . replace ( / \s + $ / g, "" ) ; // trim from end
156+ lineContent = lineContent . replace ( "\t" , " " ) ; // convert tabs to single spaces
157+
158+ // Output the gutter and the actual contents of the line.
159+ output += formatAndReset ( padLeft ( i + 1 + "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
160+ output += lineContent + sys . newLine ;
161+
162+ // Output the gutter and the error span for the line using tildes.
163+ output += formatAndReset ( padLeft ( "" , gutterWidth ) , gutterStyleSequence ) + gutterSeparator ;
164+ output += redForegroundEscapeSequence ;
165+ if ( i === firstLine ) {
166+ // If we're on the last line, then limit it to the last character of the last line.
167+ // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position.
168+ const lastCharForLine = i === lastLine ? lastLineChar : undefined ;
169+
170+ output += lineContent . slice ( 0 , firstLineChar ) . replace ( / \S / g, " " ) ;
171+ output += lineContent . slice ( firstLineChar , lastCharForLine ) . replace ( / ./ g, "~" ) ;
172+ }
173+ else if ( i === lastLine ) {
174+ output += lineContent . slice ( 0 , lastLineChar ) . replace ( / ./ g, "~" ) ;
175+ }
176+ else {
177+ // Squiggle the entire line.
178+ output += lineContent . replace ( / ./ g, "~" ) ;
179+ }
180+ output += resetEscapeSequence ;
181+
182+ output += sys . newLine ;
183+ }
184+
185+ output += sys . newLine ;
186+ output += `${ file . fileName } (${ firstLine + 1 } ,${ firstLineChar + 1 } ): ` ;
105187 }
188+
189+ const categoryColor = categoryFormatMap [ diagnostic . category ] ;
190+ const category = DiagnosticCategory [ diagnostic . category ] . toLowerCase ( ) ;
191+ output += `${ formatAndReset ( category , categoryColor ) } TS${ diagnostic . code } : ${ flattenDiagnosticMessageText ( diagnostic . messageText , sys . newLine ) } ` ;
192+ output += sys . newLine + sys . newLine ;
193+
194+ sys . write ( output ) ;
106195 }
107196
108197 function reportWatchDiagnostic ( diagnostic : Diagnostic ) {
@@ -288,6 +377,10 @@ namespace ts {
288377 compilerHost . fileExists = cachedFileExists ;
289378 }
290379
380+ if ( compilerOptions . pretty ) {
381+ reportDiagnostic = reportDiagnosticWithColorAndContext ;
382+ }
383+
291384 // reset the cache of existing files
292385 cachedExistingFiles = { } ;
293386
0 commit comments