Skip to content

Commit c404133

Browse files
authored
fix#196405 + improve notebook multi-cell highlighting (microsoft#196517)
* fix#196405 + improve notebook highlighting * remove unnecessary fxn * delete comments * redundant isDisposed check. handled by modelInfo null
1 parent 9642adb commit c404133

File tree

1 file changed

+59
-34
lines changed

1 file changed

+59
-34
lines changed

src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ interface IOccurenceAtPositionRequest {
7373
}
7474

7575
interface IWordHighlighterQuery {
76-
readonly model: ITextModel;
77-
readonly selection: Selection;
78-
readonly word: IWordAtPosition;
76+
modelInfo: {
77+
model: ITextModel;
78+
selection: Selection;
79+
} | null;
80+
readonly word: IWordAtPosition | null;
7981
}
8082

8183
abstract class OccurenceAtPositionRequest implements IOccurenceAtPositionRequest {
@@ -93,7 +95,6 @@ abstract class OccurenceAtPositionRequest implements IOccurenceAtPositionRequest
9395
this._result = createCancelablePromise(token => this._compute(this._model, this._selection, this._wordSeparators, token));
9496
}
9597
return this._result;
96-
9798
}
9899

99100
protected abstract _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>>;
@@ -177,18 +178,27 @@ class TextualOccurenceRequest extends OccurenceAtPositionRequest {
177178

178179
private readonly _otherModels: ITextModel[];
179180
private readonly _selectionIsEmpty: boolean;
181+
private readonly _word: IWordAtPosition | null;
180182

181-
constructor(model: ITextModel, selection: Selection, wordSeparators: string, otherModels: ITextModel[]) {
183+
constructor(model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]) {
182184
super(model, selection, wordSeparators);
183185
this._otherModels = otherModels;
184186
this._selectionIsEmpty = selection.isEmpty();
187+
this._word = word;
185188
}
186189

187190
protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>> {
188191
return timeout(250, token).then(() => {
189192
const result = new ResourceMap<DocumentHighlight[]>();
190-
const word = model.getWordAtPosition(selection.getPosition());
191-
if (!word) {
193+
194+
let wordResult;
195+
if (this._word) {
196+
wordResult = this._word;
197+
} else {
198+
wordResult = model.getWordAtPosition(selection.getPosition());
199+
}
200+
201+
if (!wordResult) {
192202
return new ResourceMap<DocumentHighlight[]>();
193203
}
194204

@@ -199,7 +209,7 @@ class TextualOccurenceRequest extends OccurenceAtPositionRequest {
199209
continue;
200210
}
201211

202-
const matches = otherModel.findMatches(word.word, true, false, true, wordSeparators, false);
212+
const matches = otherModel.findMatches(wordResult.word, true, false, true, wordSeparators, false);
203213
const highlights = matches.map(m => ({
204214
range: m.range,
205215
kind: DocumentHighlightKind.Text
@@ -222,18 +232,18 @@ class TextualOccurenceRequest extends OccurenceAtPositionRequest {
222232
}
223233
}
224234

225-
function computeOccurencesAtPosition(registry: LanguageFeatureRegistry<DocumentHighlightProvider>, model: ITextModel, selection: Selection, wordSeparators: string): IOccurenceAtPositionRequest {
235+
function computeOccurencesAtPosition(registry: LanguageFeatureRegistry<DocumentHighlightProvider>, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string): IOccurenceAtPositionRequest {
226236
if (registry.has(model)) {
227237
return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry);
228238
}
229-
return new TextualOccurenceRequest(model, selection, wordSeparators, []);
239+
return new TextualOccurenceRequest(model, selection, word, wordSeparators, []);
230240
}
231241

232-
function computeOccurencesMultiModel(registry: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, model: ITextModel, selection: Selection, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest {
242+
function computeOccurencesMultiModel(registry: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest {
233243
if (registry.has(model)) {
234244
return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels);
235245
}
236-
return new TextualOccurenceRequest(model, selection, wordSeparators, otherModels);
246+
return new TextualOccurenceRequest(model, selection, word, wordSeparators, otherModels);
237247
}
238248

239249
registerModelAndPositionCommand('_executeDocumentHighlights', (accessor, model, position) => {
@@ -326,9 +336,9 @@ class WordHighlighter {
326336
this.renderDecorationsTimer = -1;
327337

328338
// if there is a query already, highlight off that query
329-
// if (WordHighlighter.query) {
330-
// this._run();
331-
// }
339+
if (WordHighlighter.query) {
340+
this._run();
341+
}
332342
}
333343

334344
public hasDecorations(): boolean {
@@ -396,14 +406,6 @@ class WordHighlighter {
396406
}
397407

398408
private _removeSingleDecorations(): void {
399-
// Clear current query if editor is focused
400-
// if (this.editor.hasWidgetFocus() && this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) {
401-
if (this.editor.hasWidgetFocus() && !(WordHighlighter.query?.model.uri.scheme === Schemas.vscodeNotebookCell || this.editor.getModel()?.uri.scheme === Schemas.vscodeNotebookCell)) {
402-
// if (this.editor.hasWidgetFocus()) {
403-
// ! WordHighlighter.query?.model.dispose();
404-
WordHighlighter.query = null;
405-
}
406-
407409
// return if no model
408410
if (!this.editor.hasModel()) {
409411
return;
@@ -455,9 +457,15 @@ class WordHighlighter {
455457
// Remove any existing decorations + a possible query, and re - run to update decorations
456458
this._removeSingleDecorations();
457459

458-
// don't re-run notebooks
459-
if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) {
460-
this._run();
460+
if (this.editor.hasWidgetFocus()) {
461+
if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell && WordHighlighter.query?.modelInfo?.model.uri.scheme !== Schemas.vscodeNotebookCell) { // clear query if focused non-nb editor
462+
WordHighlighter.query = null;
463+
this._run();
464+
} else { // remove modelInfo to account for nb cell being disposed
465+
if (WordHighlighter.query?.modelInfo) {
466+
WordHighlighter.query.modelInfo = null;
467+
}
468+
}
461469
}
462470

463471
// Cancel any renderDecorationsTimer
@@ -594,7 +602,6 @@ class WordHighlighter {
594602
if (!this.editor.hasWidgetFocus()) { // no focus (new nb cell, etc)
595603
if (WordHighlighter.query === null) {
596604
// no previous query, nothing to highlight
597-
this._stopAll();
598605
return;
599606
}
600607
} else {
@@ -625,8 +632,10 @@ class WordHighlighter {
625632
workerRequestIsValid = (this.workerRequest && this.workerRequest.isValid(this.model, editorSelection, this.decorations));
626633

627634
WordHighlighter.query = {
628-
model: this.model,
629-
selection: editorSelection,
635+
modelInfo: {
636+
model: this.model,
637+
selection: editorSelection,
638+
},
630639
word: word
631640
};
632641
}
@@ -660,13 +669,21 @@ class WordHighlighter {
660669

661670
const otherModelsToHighlight = this.getOtherModelsToHighlight(this.editor.getModel());
662671

663-
if (!otherModelsToHighlight.length) {
664-
this.workerRequest = computeOccurencesAtPosition(this.providers, WordHighlighter.query.model, WordHighlighter.query.selection, this.editor.getOption(EditorOption.wordSeparators));
665-
} else {
666-
this.workerRequest = computeOccurencesMultiModel(this.multiDocumentProviders, WordHighlighter.query.model, WordHighlighter.query.selection, this.editor.getOption(EditorOption.wordSeparators), otherModelsToHighlight);
672+
// 2 cases where we want to send the word
673+
// a) there is no stored query model, but there is a word. This signals the editor that drove the highlight is disposed (cell out of viewport, etc)
674+
// b) the queried model is not the current model. This signals that the editor that drove the highlight is still in the viewport, but we are highlighting a different cell
675+
// otherwise, we send null in place of the word, and the model and selection are used to compute the word
676+
const sendWord = (!WordHighlighter.query.modelInfo && WordHighlighter.query.word) ||
677+
(WordHighlighter.query.modelInfo?.model.uri !== this.model.uri)
678+
? true : false;
679+
680+
if (!WordHighlighter.query.modelInfo || (WordHighlighter.query.modelInfo.model.uri !== this.model.uri)) { // use this.model
681+
this.workerRequest = this.computeWithModel(this.model, this.editor.getSelection(), sendWord ? WordHighlighter.query.word : null, otherModelsToHighlight);
682+
} else { // use stored query model + selection
683+
this.workerRequest = this.computeWithModel(WordHighlighter.query.modelInfo.model, WordHighlighter.query.modelInfo.selection, WordHighlighter.query.word, otherModelsToHighlight);
667684
}
668685

669-
this.workerRequest.result.then(data => {
686+
this.workerRequest?.result.then(data => {
670687
if (myRequestId === this.workerRequestTokenId) {
671688
this.workerRequestCompleted = true;
672689
this.workerRequestValue = data || [];
@@ -676,6 +693,14 @@ class WordHighlighter {
676693
}
677694
}
678695

696+
private computeWithModel(model: ITextModel, selection: Selection, word: IWordAtPosition | null, otherModels: ITextModel[]): IOccurenceAtPositionRequest | null {
697+
if (!otherModels.length) {
698+
return computeOccurencesAtPosition(this.providers, model, selection, word, this.editor.getOption(EditorOption.wordSeparators));
699+
} else {
700+
return computeOccurencesMultiModel(this.multiDocumentProviders, model, selection, word, this.editor.getOption(EditorOption.wordSeparators), otherModels);
701+
}
702+
}
703+
679704
private _beginRenderDecorations(): void {
680705
const currentTime = (new Date()).getTime();
681706
const minimumRenderTime = this.lastCursorPositionChangeTime + 250;

0 commit comments

Comments
 (0)