From 79327ff958a5583ea62d21eb318d75f449fa3255 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Wed, 31 Jul 2024 10:54:04 -0700 Subject: [PATCH] Fix all x-model highlight flickering (#224415) fix all x-model highlight flickering --- .../browser/wordHighlighter.ts | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index c959ba8c0a1a7..a566fd079de6d 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { CancelablePromise, createCancelablePromise, first, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, Delayer, first, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -267,11 +267,11 @@ class WordHighlighter { private readonly editor: IActiveCodeEditor; private readonly providers: LanguageFeatureRegistry; private readonly multiDocumentProviders: LanguageFeatureRegistry; - private occurrencesHighlight: string; private readonly model: ITextModel; private readonly decorations: IEditorDecorationsCollection; private readonly toUnhook = new DisposableStore(); private readonly codeEditorService: ICodeEditorService; + private occurrencesHighlight: string; private workerRequestTokenId: number = 0; private workerRequest: IOccurenceAtPositionRequest | null; @@ -284,7 +284,9 @@ class WordHighlighter { private readonly _hasWordHighlights: IContextKey; private _ignorePositionChangeEvent: boolean; - private static storedDecorations: ResourceMap = new ResourceMap(); + private readonly runDelayer: Delayer = this.toUnhook.add(new Delayer(50)); + + private static storedDecorationIDs: ResourceMap = new ResourceMap(); private static query: IWordHighlighterQuery | null = null; constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry, multiProviders: LanguageFeatureRegistry, contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService) { @@ -308,7 +310,7 @@ class WordHighlighter { return; } - this._onPositionChanged(e); + this.runDelayer.trigger(() => { this._onPositionChanged(e); }); })); this.toUnhook.add(editor.onDidFocusEditorText((e) => { if (this.occurrencesHighlight === 'off') { @@ -317,7 +319,7 @@ class WordHighlighter { } if (!this.workerRequest) { - this._run(); + this.runDelayer.trigger(() => { this._run(); }); } })); this.toUnhook.add(editor.onDidChangeModelContent((e) => { @@ -345,7 +347,7 @@ class WordHighlighter { break; case 'multiFile': if (WordHighlighter.query) { - this._run(); + this._run(true); } break; default: @@ -439,13 +441,13 @@ class WordHighlighter { return; } - const currentDecorationIDs = WordHighlighter.storedDecorations.get(this.editor.getModel().uri); + const currentDecorationIDs = WordHighlighter.storedDecorationIDs.get(this.editor.getModel().uri); if (!currentDecorationIDs) { return; } this.editor.removeDecorations(currentDecorationIDs); - WordHighlighter.storedDecorations.delete(this.editor.getModel().uri); + WordHighlighter.storedDecorationIDs.delete(this.editor.getModel().uri); if (this.decorations.length > 0) { this.decorations.clear(); @@ -462,7 +464,7 @@ class WordHighlighter { continue; } - const currentDecorationIDs = WordHighlighter.storedDecorations.get(editor.getModel().uri); + const currentDecorationIDs = WordHighlighter.storedDecorationIDs.get(editor.getModel().uri); if (!currentDecorationIDs) { continue; } @@ -483,7 +485,7 @@ class WordHighlighter { } for (const uri of deleteURI) { - WordHighlighter.storedDecorations.delete(uri); + WordHighlighter.storedDecorationIDs.delete(uri); } } @@ -639,13 +641,14 @@ class WordHighlighter { return currentModels; } - private _run(): void { + private _run(multiFileConfigChange?: boolean): void { let workerRequestIsValid; const hasTextFocus = this.editor.hasTextFocus(); if (!hasTextFocus) { // new nb cell scrolled in, didChangeModel fires if (!WordHighlighter.query) { // no previous query, nothing to highlight off of + this._stopAll(); return; } } else { // has text focus @@ -705,10 +708,22 @@ class WordHighlighter { this.renderDecorationsTimer = -1; this._beginRenderDecorations(); } - } else { + } else if (isEqual(this.editor.getModel().uri, WordHighlighter.query.modelInfo?.model.uri)) { // only trigger new worker requests from the primary model that initiated the query // case d) - // Stop all previous actions and start fresh - this._stopAll(); + + // check if the new queried word is contained in the range of a stored decoration for this model + if (!multiFileConfigChange) { + const currentModelDecorationRanges = this.decorations.getRanges(); + for (const storedRange of currentModelDecorationRanges) { + if (storedRange.containsPosition(this.editor.getPosition())) { + return; + } + } + } + + // stop all previous actions if new word is highlighted + // if we trigger the run off a setting change -> multifile highlighting, we do not want to remove decorations from this model + this._stopAll(multiFileConfigChange ? this.model : undefined); const myRequestId = ++this.workerRequestTokenId; this.workerRequestCompleted = false; @@ -773,7 +788,7 @@ class WordHighlighter { const newDecorations: IModelDeltaDecoration[] = []; const uri = editor.getModel()?.uri; if (uri && this.workerRequestValue.has(uri)) { - const oldDecorationIDs: string[] | undefined = WordHighlighter.storedDecorations.get(uri); + const oldDecorationIDs: string[] | undefined = WordHighlighter.storedDecorationIDs.get(uri); const newDocumentHighlights = this.workerRequestValue.get(uri); if (newDocumentHighlights) { for (const highlight of newDocumentHighlights) { @@ -791,7 +806,7 @@ class WordHighlighter { editor.changeDecorations((changeAccessor) => { newDecorationIDs = changeAccessor.deltaDecorations(oldDecorationIDs ?? [], newDecorations); }); - WordHighlighter.storedDecorations = WordHighlighter.storedDecorations.set(uri, newDecorationIDs); + WordHighlighter.storedDecorationIDs = WordHighlighter.storedDecorationIDs.set(uri, newDecorationIDs); if (newDecorations.length > 0) { editorHighlighterContrib.wordHighlighter?.decorations.set(newDecorations);