@@ -125,7 +125,7 @@ function replaceWsWithNoBreakWs(str: string): string {
125
125
return str . replace ( / [ \t ] / g, strings . noBreakWhitespace ) ;
126
126
}
127
127
128
- function createInlineValueDecorationsInsideRange ( expressions : ReadonlyArray < IExpression > , range : Range , model : ITextModel , wordToLineNumbersMap : Map < string , number [ ] > ) : IModelDeltaDecoration [ ] {
128
+ function createInlineValueDecorationsInsideRange ( expressions : ReadonlyArray < IExpression > , ranges : Range [ ] , model : ITextModel , wordToLineNumbersMap : Map < string , number [ ] > ) : IModelDeltaDecoration [ ] {
129
129
const nameValueMap = new Map < string , string > ( ) ;
130
130
for ( const expr of expressions ) {
131
131
nameValueMap . set ( expr . name , expr . value ) ;
@@ -142,7 +142,7 @@ function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray<IExp
142
142
const lineNumbers = wordToLineNumbersMap . get ( name ) ;
143
143
if ( lineNumbers ) {
144
144
for ( const lineNumber of lineNumbers ) {
145
- if ( range . containsPosition ( new Position ( lineNumber , 0 ) ) ) {
145
+ if ( ranges . some ( r => lineNumber >= r . startLineNumber && lineNumber <= r . endLineNumber ) ) {
146
146
if ( ! lineToNamesMap . has ( lineNumber ) ) {
147
147
lineToNamesMap . set ( lineNumber , [ ] ) ;
148
148
}
@@ -168,49 +168,39 @@ function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray<IExp
168
168
return decorations ;
169
169
}
170
170
171
- function getWordToLineNumbersMap ( model : ITextModel | null ) : Map < string , number [ ] > {
172
- const result = new Map < string , number [ ] > ( ) ;
173
- if ( ! model ) {
174
- return result ;
171
+ function getWordToLineNumbersMap ( model : ITextModel , lineNumber : number , result : Map < string , number [ ] > ) {
172
+ const lineLength = model . getLineLength ( lineNumber ) ;
173
+ // If line is too long then skip the line
174
+ if ( lineLength > MAX_TOKENIZATION_LINE_LEN ) {
175
+ return ;
175
176
}
176
177
177
- // For every word in every line, map its ranges for fast lookup
178
- for ( let lineNumber = 1 , len = model . getLineCount ( ) ; lineNumber <= len ; ++ lineNumber ) {
179
- const lineLength = model . getLineLength ( lineNumber ) ;
180
- // If line is too long then skip the line
181
- if ( lineLength > MAX_TOKENIZATION_LINE_LEN ) {
182
- continue ;
183
- }
184
-
185
- const lineContent = model . getLineContent ( lineNumber ) ;
186
- model . tokenization . forceTokenization ( lineNumber ) ;
187
- const lineTokens = model . tokenization . getLineTokens ( lineNumber ) ;
188
- for ( let tokenIndex = 0 , tokenCount = lineTokens . getCount ( ) ; tokenIndex < tokenCount ; tokenIndex ++ ) {
189
- const tokenType = lineTokens . getStandardTokenType ( tokenIndex ) ;
178
+ const lineContent = model . getLineContent ( lineNumber ) ;
179
+ model . tokenization . forceTokenization ( lineNumber ) ;
180
+ const lineTokens = model . tokenization . getLineTokens ( lineNumber ) ;
181
+ for ( let tokenIndex = 0 , tokenCount = lineTokens . getCount ( ) ; tokenIndex < tokenCount ; tokenIndex ++ ) {
182
+ const tokenType = lineTokens . getStandardTokenType ( tokenIndex ) ;
190
183
191
- // Token is a word and not a comment
192
- if ( tokenType === StandardTokenType . Other ) {
193
- DEFAULT_WORD_REGEXP . lastIndex = 0 ; // We assume tokens will usually map 1:1 to words if they match
184
+ // Token is a word and not a comment
185
+ if ( tokenType === StandardTokenType . Other ) {
186
+ DEFAULT_WORD_REGEXP . lastIndex = 0 ; // We assume tokens will usually map 1:1 to words if they match
194
187
195
- const tokenStartOffset = lineTokens . getStartOffset ( tokenIndex ) ;
196
- const tokenEndOffset = lineTokens . getEndOffset ( tokenIndex ) ;
197
- const tokenStr = lineContent . substring ( tokenStartOffset , tokenEndOffset ) ;
198
- const wordMatch = DEFAULT_WORD_REGEXP . exec ( tokenStr ) ;
188
+ const tokenStartOffset = lineTokens . getStartOffset ( tokenIndex ) ;
189
+ const tokenEndOffset = lineTokens . getEndOffset ( tokenIndex ) ;
190
+ const tokenStr = lineContent . substring ( tokenStartOffset , tokenEndOffset ) ;
191
+ const wordMatch = DEFAULT_WORD_REGEXP . exec ( tokenStr ) ;
199
192
200
- if ( wordMatch ) {
193
+ if ( wordMatch ) {
201
194
202
- const word = wordMatch [ 0 ] ;
203
- if ( ! result . has ( word ) ) {
204
- result . set ( word , [ ] ) ;
205
- }
206
-
207
- result . get ( word ) ! . push ( lineNumber ) ;
195
+ const word = wordMatch [ 0 ] ;
196
+ if ( ! result . has ( word ) ) {
197
+ result . set ( word , [ ] ) ;
208
198
}
199
+
200
+ result . get ( word ) ! . push ( lineNumber ) ;
209
201
}
210
202
}
211
203
}
212
-
213
- return result ;
214
204
}
215
205
216
206
export class DebugEditorContribution implements IDebugEditorContribution {
@@ -310,13 +300,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
310
300
this . updateHoverConfiguration ( ) ;
311
301
}
312
302
313
- private _wordToLineNumbersMap : Map < string , number [ ] > | undefined = undefined ;
314
- private get wordToLineNumbersMap ( ) : Map < string , number [ ] > {
315
- if ( ! this . _wordToLineNumbersMap ) {
316
- this . _wordToLineNumbersMap = getWordToLineNumbersMap ( this . editor . getModel ( ) ) ;
317
- }
318
- return this . _wordToLineNumbersMap ;
319
- }
303
+ private _wordToLineNumbersMap : WordsToLineNumbersCache | undefined ;
320
304
321
305
private updateHoverConfiguration ( ) : void {
322
306
const model = this . editor . getModel ( ) ;
@@ -465,6 +449,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
465
449
this . hoverWidget . hide ( ) ;
466
450
}
467
451
this . showHoverScheduler . cancel ( ) ;
452
+ this . defaultHoverLockout . clear ( ) ;
468
453
}
469
454
470
455
// hover business
@@ -686,6 +671,7 @@ export class DebugEditorContribution implements IDebugEditorContribution {
686
671
687
672
this . removeInlineValuesScheduler . cancel ( ) ;
688
673
674
+ const viewRanges = this . editor . getVisibleRangesPlusViewportAboveBelow ( ) ;
689
675
let allDecorations : IModelDeltaDecoration [ ] ;
690
676
691
677
if ( this . languageFeaturesService . inlineValuesProvider . has ( model ) ) {
@@ -709,13 +695,12 @@ export class DebugEditorContribution implements IDebugEditorContribution {
709
695
} ;
710
696
const token = new CancellationTokenSource ( ) . token ;
711
697
712
- const ranges = this . editor . getVisibleRangesPlusViewportAboveBelow ( ) ;
713
698
const providers = this . languageFeaturesService . inlineValuesProvider . ordered ( model ) . reverse ( ) ;
714
699
715
700
allDecorations = [ ] ;
716
701
const lineDecorations = new Map < number , InlineSegment [ ] > ( ) ;
717
702
718
- const promises = flatten ( providers . map ( provider => ranges . map ( range => Promise . resolve ( provider . provideInlineValues ( model , range , ctx , token ) ) . then ( async ( result ) => {
703
+ const promises = flatten ( providers . map ( provider => viewRanges . map ( range => Promise . resolve ( provider . provideInlineValues ( model , range , ctx , token ) ) . then ( async ( result ) => {
719
704
if ( result ) {
720
705
for ( const iv of result ) {
721
706
@@ -795,12 +780,18 @@ export class DebugEditorContribution implements IDebugEditorContribution {
795
780
const decorationsPerScope = await Promise . all ( scopes . map ( async scope => {
796
781
const variables = await scope . getChildren ( ) ;
797
782
798
- let range = new Range ( 0 , 0 , stackFrame . range . startLineNumber , stackFrame . range . startColumn ) ;
783
+ let scopeRange = new Range ( 0 , 0 , stackFrame . range . startLineNumber , stackFrame . range . startColumn ) ;
799
784
if ( scope . range ) {
800
- range = range . setStartPosition ( scope . range . startLineNumber , scope . range . startColumn ) ;
785
+ scopeRange = scopeRange . setStartPosition ( scope . range . startLineNumber , scope . range . startColumn ) ;
801
786
}
802
787
803
- return createInlineValueDecorationsInsideRange ( variables , range , model , this . wordToLineNumbersMap ) ;
788
+ const ownRanges = viewRanges . map ( r => r . intersectRanges ( scopeRange ) ) . filter ( isDefined ) ;
789
+ this . _wordToLineNumbersMap ??= new WordsToLineNumbersCache ( model ) ;
790
+ for ( const range of ownRanges ) {
791
+ this . _wordToLineNumbersMap . ensureRangePopulated ( range ) ;
792
+ }
793
+
794
+ return createInlineValueDecorationsInsideRange ( variables , ownRanges , model , this . _wordToLineNumbersMap . value ) ;
804
795
} ) ) ;
805
796
806
797
allDecorations = distinct ( decorationsPerScope . reduce ( ( previous , current ) => previous . concat ( current ) , [ ] ) ,
@@ -824,6 +815,28 @@ export class DebugEditorContribution implements IDebugEditorContribution {
824
815
}
825
816
}
826
817
818
+ class WordsToLineNumbersCache {
819
+ // we use this as an array of bits where each 1 bit is a line number that's been parsed
820
+ private readonly intervals : Uint8Array ;
821
+ public readonly value = new Map < string , number [ ] > ( ) ;
822
+
823
+ constructor ( private readonly model : ITextModel ) {
824
+ this . intervals = new Uint8Array ( Math . ceil ( model . getLineCount ( ) / 8 ) ) ;
825
+ }
826
+
827
+ /** Ensures that variables names in the given range have been identified. */
828
+ public ensureRangePopulated ( range : Range ) {
829
+ for ( let lineNumber = range . startLineNumber ; lineNumber <= range . endLineNumber ; lineNumber ++ ) {
830
+ const bin = lineNumber >> 3 ; /* Math.floor(i / 8) */
831
+ const bit = 1 << ( lineNumber & 0b111 ) ; /* 1 << (i % 8) */
832
+ if ( ! ( this . intervals [ bin ] & bit ) ) {
833
+ getWordToLineNumbersMap ( this . model , lineNumber , this . value ) ;
834
+ this . intervals [ bin ] |= bit ;
835
+ }
836
+ }
837
+ }
838
+ }
839
+
827
840
828
841
CommandsRegistry . registerCommand (
829
842
'_executeInlineValueProvider' ,
0 commit comments