@@ -6,6 +6,14 @@ namespace ts {
6
6
fileWatcher ?: FileWatcher ;
7
7
}
8
8
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
+
9
17
/**
10
18
* Checks to see if the locale is in the appropriate format,
11
19
* and if it is, attempts to set the appropriate language.
@@ -81,16 +89,16 @@ namespace ts {
81
89
return < string > diagnostic . messageText ;
82
90
}
83
91
84
- function reportDiagnostic ( diagnostic : Diagnostic , host : CompilerHost ) {
92
+ function reportDiagnosticSimply ( diagnostic : Diagnostic , host : CompilerHost ) : void {
85
93
let output = "" ;
86
94
87
95
if ( diagnostic . file ) {
88
- let loc = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
96
+ const { line , character } = getLineAndCharacterOfPosition ( diagnostic . file , diagnostic . start ) ;
89
97
const relativeFileName = host
90
98
? convertToRelativePath ( diagnostic . file . fileName , host . getCurrentDirectory ( ) , fileName => host . getCanonicalFileName ( fileName ) )
91
99
: diagnostic . file . fileName ;
92
100
93
- output += `${ relativeFileName } (${ loc . line + 1 } ,${ loc . character + 1 } ): ` ;
101
+ output += `${ diagnostic . file . fileName } (${ line + 1 } ,${ character + 1 } ): ` ;
94
102
}
95
103
96
104
let category = DiagnosticCategory [ diagnostic . category ] . toLowerCase ( ) ;
@@ -99,10 +107,91 @@ namespace ts {
99
107
sys . write ( output ) ;
100
108
}
101
109
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 } ): ` ;
105
187
}
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 ) ;
106
195
}
107
196
108
197
function reportWatchDiagnostic ( diagnostic : Diagnostic ) {
@@ -288,6 +377,10 @@ namespace ts {
288
377
compilerHost . fileExists = cachedFileExists ;
289
378
}
290
379
380
+ if ( compilerOptions . pretty ) {
381
+ reportDiagnostic = reportDiagnosticWithColorAndContext ;
382
+ }
383
+
291
384
// reset the cache of existing files
292
385
cachedExistingFiles = { } ;
293
386
0 commit comments