@@ -152,18 +152,44 @@ export class TwoslashModule extends Module {
152
152
e => '// ' + e . renderedMessage . split ( '\n' ) . join ( '\n// ' ) ,
153
153
) ;
154
154
155
- // ^^^^^ ^^^^^
156
- // hats to indicate what token is causing the issue
157
- let linkWithHats = '' ;
158
- lineErrors . forEach ( e => {
159
- if ( ! e . character ) return ;
160
- const spaceBefore = e . character - linkWithHats . length ;
161
- linkWithHats += ' ' . repeat ( spaceBefore ) ;
162
- linkWithHats += '^' . repeat ( e . length || 0 ) ;
163
- } ) ;
164
-
165
- if ( linkWithHats . length > 0 ) {
166
- resultLines . push ( '//' + linkWithHats . substr ( 2 ) ) ;
155
+ // Points to errors, e.g. ' ^^^^ ^^^^^'
156
+ const hats = lineErrors
157
+ // only those with a valid span
158
+ . filter ( x => x . character != null && x . length != null )
159
+ // map to [start, end (non-inclusive)]
160
+ . map (
161
+ error =>
162
+ [
163
+ error . character ! ,
164
+ error . character ! + error . length ! ,
165
+ ] as const ,
166
+ )
167
+ // sort by start, ascending
168
+ . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] )
169
+ // fix overlapping ranges
170
+ . map ( ( cur , i , a ) => {
171
+ let prev = a [ i - 1 ] ;
172
+ if ( ! prev ) return cur ;
173
+ return [
174
+ Math . max ( prev [ 1 ] , cur [ 0 ] ) ,
175
+ Math . max ( prev [ 1 ] , cur [ 1 ] ) ,
176
+ ] as const ;
177
+ } )
178
+ // remove empty ranges
179
+ . filter ( ( [ start , end ] ) => start < end )
180
+ // map each to hats
181
+ . map ( ( [ start , end ] , i , a ) => {
182
+ let prevEnd = a [ i - 1 ] ?. [ 1 ] ?? 0 ;
183
+ return (
184
+ ' ' . repeat ( start - prevEnd ) +
185
+ '^' . repeat ( end - start )
186
+ ) ;
187
+ } )
188
+ // join the resulting strings
189
+ . join ( '' ) ;
190
+
191
+ if ( hats . length > 0 ) {
192
+ resultLines . push ( '//' + hats . slice ( 2 ) ) ;
167
193
}
168
194
169
195
resultLines . push ( ...errors ) ;
0 commit comments