Support Multi Editor/Model Highlighting (#196024)

* support multi-editor highlighting

* Add experimental multi-document occurrences highlighting option

* highlight accross notebook cells even without multi-doc setting

* otherModels helper + ResourceMap + fixed removeDecorations

* combine textual to just `TextualOccurenceRequest`

* fix aggressive dispose/decoration clearing, introduce query interface
This commit is contained in:
Michael Lively
2023-10-24 06:27:02 +02:00
committed by GitHub
parent 9e597d3227
commit 7dff07893a
10 changed files with 603 additions and 272 deletions
@@ -562,6 +562,11 @@ export interface IEditorOptions {
* Defaults to true.
*/
occurrencesHighlight?: boolean;
/**
* Enable semantic occurrences highlight.
* Defaults to true.
*/
multiDocumentOccurrencesHighlight?: boolean;
/**
* Show code lens
* Defaults to true.
@@ -5116,6 +5121,7 @@ export const enum EditorOption {
multiCursorModifier,
multiCursorPaste,
multiCursorLimit,
multiDocumentOccurrencesHighlight,
occurrencesHighlight,
overviewRulerBorder,
overviewRulerLanes,
@@ -5616,6 +5622,10 @@ export const EditorOptions = {
EditorOption.occurrencesHighlight, 'occurrencesHighlight', true,
{ description: nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences.") }
)),
multiDocumentOccurrencesHighlight: register(new EditorBooleanOption(
EditorOption.multiDocumentOccurrencesHighlight, 'multiDocumentOccurrencesHighlight', false,
{ description: nls.localize('multiDocumentOccurrencesHighlight', "Experimental: Controls whether the editor should highlight word occurrences accross multiple open editors.") }
)),
overviewRulerBorder: register(new EditorBooleanOption(
EditorOption.overviewRulerBorder, 'overviewRulerBorder', true,
{ description: nls.localize('overviewRulerBorder', "Controls whether a border should be drawn around the overview ruler.") }
+11
View File
@@ -965,6 +965,17 @@ export interface DocumentHighlightProvider {
provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<DocumentHighlight[]>;
}
export interface MultiDocumentHighlightProvider {
/**
* Provide a Map of URI --> document highlights, like all occurrences of a variable or
* all exit-points of a function.
*
* Used in cases such as split view, notebooks, etc. where there can be multiple documents
* with shared symbols.
*/
provideMultiDocumentHighlights(primaryModel: model.ITextModel, position: Position, otherModels: model.ITextModel[], token: CancellationToken): ProviderResult<Map<URI, DocumentHighlight[]>>;
}
/**
* The linked editing range provider interface defines the contract between extensions and
* the linked editing feature.
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { LanguageFeatureRegistry, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, MappedEditsProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, MappedEditsProvider, MultiDocumentHighlightProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider } from 'vs/editor/common/languages';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ILanguageFeaturesService = createDecorator<ILanguageFeaturesService>('ILanguageFeaturesService');
@@ -49,6 +49,8 @@ export interface ILanguageFeaturesService {
readonly documentHighlightProvider: LanguageFeatureRegistry<DocumentHighlightProvider>;
readonly multiDocumentHighlightProvider: LanguageFeatureRegistry<MultiDocumentHighlightProvider>;
readonly documentRangeSemanticTokensProvider: LanguageFeatureRegistry<DocumentRangeSemanticTokensProvider>;
readonly documentSemanticTokensProvider: LanguageFeatureRegistry<DocumentSemanticTokensProvider>;
@@ -5,7 +5,7 @@
import { URI } from 'vs/base/common/uri';
import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from 'vs/editor/common/languageFeatureRegistry';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, MappedEditsProvider } from 'vs/editor/common/languages';
import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, MultiDocumentHighlightProvider, DocumentHighlightProvider, DocumentOnDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, MappedEditsProvider } from 'vs/editor/common/languages';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -30,6 +30,7 @@ export class LanguageFeaturesService implements ILanguageFeaturesService {
readonly signatureHelpProvider = new LanguageFeatureRegistry<SignatureHelpProvider>(this._score.bind(this));
readonly hoverProvider = new LanguageFeatureRegistry<HoverProvider>(this._score.bind(this));
readonly documentHighlightProvider = new LanguageFeatureRegistry<DocumentHighlightProvider>(this._score.bind(this));
readonly multiDocumentHighlightProvider = new LanguageFeatureRegistry<MultiDocumentHighlightProvider>(this._score.bind(this));
readonly selectionRangeProvider = new LanguageFeatureRegistry<SelectionRangeProvider>(this._score.bind(this));
readonly foldingRangeProvider = new LanguageFeatureRegistry<FoldingRangeProvider>(this._score.bind(this));
readonly linkProvider = new LanguageFeatureRegistry<LinkProvider>(this._score.bind(this));
@@ -253,74 +253,75 @@ export enum EditorOption {
multiCursorModifier = 77,
multiCursorPaste = 78,
multiCursorLimit = 79,
occurrencesHighlight = 80,
overviewRulerBorder = 81,
overviewRulerLanes = 82,
padding = 83,
pasteAs = 84,
parameterHints = 85,
peekWidgetDefaultFocus = 86,
definitionLinkOpensInPeek = 87,
quickSuggestions = 88,
quickSuggestionsDelay = 89,
readOnly = 90,
readOnlyMessage = 91,
renameOnType = 92,
renderControlCharacters = 93,
renderFinalNewline = 94,
renderLineHighlight = 95,
renderLineHighlightOnlyWhenFocus = 96,
renderValidationDecorations = 97,
renderWhitespace = 98,
revealHorizontalRightPadding = 99,
roundedSelection = 100,
rulers = 101,
scrollbar = 102,
scrollBeyondLastColumn = 103,
scrollBeyondLastLine = 104,
scrollPredominantAxis = 105,
selectionClipboard = 106,
selectionHighlight = 107,
selectOnLineNumbers = 108,
showFoldingControls = 109,
showUnused = 110,
snippetSuggestions = 111,
smartSelect = 112,
smoothScrolling = 113,
stickyScroll = 114,
stickyTabStops = 115,
stopRenderingLineAfter = 116,
suggest = 117,
suggestFontSize = 118,
suggestLineHeight = 119,
suggestOnTriggerCharacters = 120,
suggestSelection = 121,
tabCompletion = 122,
tabIndex = 123,
unicodeHighlighting = 124,
unusualLineTerminators = 125,
useShadowDOM = 126,
useTabStops = 127,
wordBreak = 128,
wordSeparators = 129,
wordWrap = 130,
wordWrapBreakAfterCharacters = 131,
wordWrapBreakBeforeCharacters = 132,
wordWrapColumn = 133,
wordWrapOverride1 = 134,
wordWrapOverride2 = 135,
wrappingIndent = 136,
wrappingStrategy = 137,
showDeprecated = 138,
inlayHints = 139,
editorClassName = 140,
pixelRatio = 141,
tabFocusMode = 142,
layoutInfo = 143,
wrappingInfo = 144,
defaultColorDecorators = 145,
colorDecoratorsActivatedOn = 146,
inlineCompletionsAccessibilityVerbose = 147
multiDocumentOccurrencesHighlight = 80,
occurrencesHighlight = 81,
overviewRulerBorder = 82,
overviewRulerLanes = 83,
padding = 84,
pasteAs = 85,
parameterHints = 86,
peekWidgetDefaultFocus = 87,
definitionLinkOpensInPeek = 88,
quickSuggestions = 89,
quickSuggestionsDelay = 90,
readOnly = 91,
readOnlyMessage = 92,
renameOnType = 93,
renderControlCharacters = 94,
renderFinalNewline = 95,
renderLineHighlight = 96,
renderLineHighlightOnlyWhenFocus = 97,
renderValidationDecorations = 98,
renderWhitespace = 99,
revealHorizontalRightPadding = 100,
roundedSelection = 101,
rulers = 102,
scrollbar = 103,
scrollBeyondLastColumn = 104,
scrollBeyondLastLine = 105,
scrollPredominantAxis = 106,
selectionClipboard = 107,
selectionHighlight = 108,
selectOnLineNumbers = 109,
showFoldingControls = 110,
showUnused = 111,
snippetSuggestions = 112,
smartSelect = 113,
smoothScrolling = 114,
stickyScroll = 115,
stickyTabStops = 116,
stopRenderingLineAfter = 117,
suggest = 118,
suggestFontSize = 119,
suggestLineHeight = 120,
suggestOnTriggerCharacters = 121,
suggestSelection = 122,
tabCompletion = 123,
tabIndex = 124,
unicodeHighlighting = 125,
unusualLineTerminators = 126,
useShadowDOM = 127,
useTabStops = 128,
wordBreak = 129,
wordSeparators = 130,
wordWrap = 131,
wordWrapBreakAfterCharacters = 132,
wordWrapBreakBeforeCharacters = 133,
wordWrapColumn = 134,
wordWrapOverride1 = 135,
wordWrapOverride2 = 136,
wrappingIndent = 137,
wrappingStrategy = 138,
showDeprecated = 139,
inlayHints = 140,
editorClassName = 141,
pixelRatio = 142,
tabFocusMode = 143,
layoutInfo = 144,
wrappingInfo = 145,
defaultColorDecorators = 146,
colorDecoratorsActivatedOn = 147,
inlineCompletionsAccessibilityVerbose = 148
}
/**
@@ -3,38 +3,39 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { alert } from 'vs/base/browser/ui/aria/aria';
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 { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, ICodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents';
import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages';
import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider } from 'vs/editor/common/languages';
import * as nls from 'vs/nls';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations';
import { Iterable } from 'vs/base/common/iterator';
import { Schemas } from 'vs/base/common/network';
import { ResourceMap } from 'vs/base/common/map';
const ctxHasWordHighlights = new RawContextKey<boolean>('hasWordHighlights', false);
export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry<DocumentHighlightProvider>, model: ITextModel, position: Position, token: CancellationToken): Promise<DocumentHighlight[] | null | undefined> {
export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry<DocumentHighlightProvider>, model: ITextModel, position: Position, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]> | null | undefined> {
const orderedByScore = registry.ordered(model);
// in order of score ask the occurrences provider
@@ -43,19 +44,44 @@ export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry<Docum
return first<DocumentHighlight[] | null | undefined>(orderedByScore.map(provider => () => {
return Promise.resolve(provider.provideDocumentHighlights(model, position, token))
.then(undefined, onUnexpectedExternalError);
}), arrays.isNonEmptyArray);
}), arrays.isNonEmptyArray).then(result => {
if (result) {
const map = new ResourceMap<DocumentHighlight[]>();
map.set(model.uri, result);
return map;
}
return new ResourceMap<DocumentHighlight[]>();
});
}
export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, model: ITextModel, position: Position, wordSeparators: string, token: CancellationToken, otherModels: ITextModel[]): Promise<ResourceMap<DocumentHighlight[]> | null | undefined> {
const orderedByScore = registry.ordered(model);
// in order of score ask the occurrences provider
// until someone response with a good result
// (good = none empty array)
return first<ResourceMap<DocumentHighlight[]> | null | undefined>(orderedByScore.map(provider => () => {
return Promise.resolve(provider.provideMultiDocumentHighlights(model, position, otherModels, token))
.then(undefined, onUnexpectedExternalError);
}), (t: ResourceMap<DocumentHighlight[]> | null | undefined): t is ResourceMap<DocumentHighlight[]> => t instanceof Map && t.size > 0);
}
interface IOccurenceAtPositionRequest {
readonly result: Promise<DocumentHighlight[]>;
readonly result: Promise<ResourceMap<DocumentHighlight[]>>;
isValid(model: ITextModel, selection: Selection, decorations: IEditorDecorationsCollection): boolean;
cancel(): void;
}
interface IWordHighlighterQuery {
readonly model: ITextModel;
readonly selection: Selection;
readonly word: IWordAtPosition;
}
abstract class OccurenceAtPositionRequest implements IOccurenceAtPositionRequest {
private readonly _wordRange: Range | null;
private _result: CancelablePromise<DocumentHighlight[]> | null;
private _result: CancelablePromise<ResourceMap<DocumentHighlight[]>> | null;
constructor(private readonly _model: ITextModel, private readonly _selection: Selection, private readonly _wordSeparators: string) {
this._wordRange = this._getCurrentWordRange(_model, _selection);
@@ -70,7 +96,7 @@ abstract class OccurenceAtPositionRequest implements IOccurenceAtPositionRequest
}
protected abstract _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<DocumentHighlight[]>;
protected abstract _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>>;
private _getCurrentWordRange(model: ITextModel, selection: Selection): Range | null {
const word = model.getWordAtPosition(selection.getPosition());
@@ -117,38 +143,73 @@ class SemanticOccurenceAtPositionRequest extends OccurenceAtPositionRequest {
this._providers = providers;
}
protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<DocumentHighlight[]> {
return getOccurrencesAtPosition(this._providers, model, selection.getPosition(), token).then(value => value || []);
protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>> {
return getOccurrencesAtPosition(this._providers, model, selection.getPosition(), token).then(value => {
if (!value) {
return new ResourceMap<DocumentHighlight[]>();
}
return value;
});
}
}
class TextualOccurenceAtPositionRequest extends OccurenceAtPositionRequest {
class MultiModelOccurenceRequest extends OccurenceAtPositionRequest {
private readonly _providers: LanguageFeatureRegistry<MultiDocumentHighlightProvider>;
private readonly _otherModels: ITextModel[];
constructor(model: ITextModel, selection: Selection, wordSeparators: string, providers: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, otherModels: ITextModel[]) {
super(model, selection, wordSeparators);
this._providers = providers;
this._otherModels = otherModels;
}
protected override _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>> {
return getOccurrencesAcrossMultipleModels(this._providers, model, selection.getPosition(), wordSeparators, token, this._otherModels).then(value => {
if (!value) {
return new ResourceMap<DocumentHighlight[]>();
}
return value;
});
}
}
class TextualOccurenceRequest extends OccurenceAtPositionRequest {
private readonly _otherModels: ITextModel[];
private readonly _selectionIsEmpty: boolean;
constructor(model: ITextModel, selection: Selection, wordSeparators: string) {
constructor(model: ITextModel, selection: Selection, wordSeparators: string, otherModels: ITextModel[]) {
super(model, selection, wordSeparators);
this._otherModels = otherModels;
this._selectionIsEmpty = selection.isEmpty();
}
protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<DocumentHighlight[]> {
protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise<ResourceMap<DocumentHighlight[]>> {
return timeout(250, token).then(() => {
if (!selection.isEmpty()) {
return [];
}
const result = new ResourceMap<DocumentHighlight[]>();
const word = model.getWordAtPosition(selection.getPosition());
if (!word || word.word.length > 1000) {
return [];
if (!word) {
return new ResourceMap<DocumentHighlight[]>();
}
const matches = model.findMatches(word.word, true, false, true, wordSeparators, false);
return matches.map(m => {
return {
const allModels = [model, ...this._otherModels];
for (const otherModel of allModels) {
if (otherModel.isDisposed()) {
continue;
}
const matches = otherModel.findMatches(word.word, true, false, true, wordSeparators, false);
const highlights = matches.map(m => ({
range: m.range,
kind: DocumentHighlightKind.Text
};
});
}));
if (highlights) {
result.set(otherModel.uri, highlights);
}
}
return result;
});
}
@@ -165,7 +226,14 @@ function computeOccurencesAtPosition(registry: LanguageFeatureRegistry<DocumentH
if (registry.has(model)) {
return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry);
}
return new TextualOccurenceAtPositionRequest(model, selection, wordSeparators);
return new TextualOccurenceRequest(model, selection, wordSeparators, []);
}
function computeOccurencesMultiModel(registry: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, model: ITextModel, selection: Selection, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest {
if (registry.has(model)) {
return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels);
}
return new TextualOccurenceRequest(model, selection, wordSeparators, otherModels);
}
registerModelAndPositionCommand('_executeDocumentHighlights', (accessor, model, position) => {
@@ -177,15 +245,18 @@ class WordHighlighter {
private readonly editor: IActiveCodeEditor;
private readonly providers: LanguageFeatureRegistry<DocumentHighlightProvider>;
private readonly multiDocumentProviders: LanguageFeatureRegistry<MultiDocumentHighlightProvider>;
private occurrencesHighlight: boolean;
private multiDocumentOccurrencesHighlight: boolean;
private readonly model: ITextModel;
private readonly decorations: IEditorDecorationsCollection;
private readonly toUnhook = new DisposableStore();
private readonly codeEditorService: ICodeEditorService;
private workerRequestTokenId: number = 0;
private workerRequest: IOccurenceAtPositionRequest | null;
private workerRequestCompleted: boolean = false;
private workerRequestValue: DocumentHighlight[] = [];
private workerRequestValue: ResourceMap<DocumentHighlight[]> = new ResourceMap();
private lastCursorPositionChangeTime: number = 0;
private renderDecorationsTimer: any = -1;
@@ -193,18 +264,20 @@ class WordHighlighter {
private readonly _hasWordHighlights: IContextKey<boolean>;
private _ignorePositionChangeEvent: boolean;
private readonly linkedHighlighters: () => Iterable<WordHighlighter | null>;
private static storedDecorations: ResourceMap<string[]> = new ResourceMap();
private static query: IWordHighlighterQuery | null = null;
constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry<DocumentHighlightProvider>, linkedHighlighters: () => Iterable<WordHighlighter | null>, contextKeyService: IContextKeyService) {
constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry<DocumentHighlightProvider>, multiProviders: LanguageFeatureRegistry<MultiDocumentHighlightProvider>, contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService) {
this.editor = editor;
this.providers = providers;
this.linkedHighlighters = linkedHighlighters;
this.multiDocumentProviders = multiProviders;
this.codeEditorService = codeEditorService;
this._hasWordHighlights = ctxHasWordHighlights.bindTo(contextKeyService);
this._ignorePositionChangeEvent = false;
this.occurrencesHighlight = this.editor.getOption(EditorOption.occurrencesHighlight);
this.multiDocumentOccurrencesHighlight = this.editor.getOption(EditorOption.multiDocumentOccurrencesHighlight);
this.model = this.editor.getModel();
this.toUnhook.add(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => {
if (this._ignorePositionChangeEvent) {
// We are changing the position => ignore this event
return;
@@ -221,12 +294,27 @@ class WordHighlighter {
this.toUnhook.add(editor.onDidChangeModelContent((e) => {
this._stopAll();
}));
this.toUnhook.add(editor.onDidChangeModel((e) => {
if (!e.newModelUrl && e.oldModelUrl) {
this._stopSingular();
} else {
if (WordHighlighter.query) {
this._run();
}
}
}));
this.toUnhook.add(editor.onDidChangeConfiguration((e) => {
const newValue = this.editor.getOption(EditorOption.occurrencesHighlight);
if (this.occurrencesHighlight !== newValue) {
this.occurrencesHighlight = newValue;
this._stopAll();
}
const newMultiDocumentValue = this.editor.getOption(EditorOption.multiDocumentOccurrencesHighlight);
if (this.multiDocumentOccurrencesHighlight !== newMultiDocumentValue) {
this.multiDocumentOccurrencesHighlight = newMultiDocumentValue;
this._stopAll();
}
}));
this.decorations = this.editor.createDecorationsCollection();
@@ -236,6 +324,11 @@ class WordHighlighter {
this.lastCursorPositionChangeTime = 0;
this.renderDecorationsTimer = -1;
// if there is a query already, highlight off that query
// if (WordHighlighter.query) {
// this._run();
// }
}
public hasDecorations(): boolean {
@@ -302,17 +395,93 @@ class WordHighlighter {
}
}
private _removeDecorations(): void {
private _removeSingleDecorations(): void {
// Clear current query if editor is focused
// if (this.editor.hasWidgetFocus() && this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) {
if (this.editor.hasWidgetFocus() && !(WordHighlighter.query?.model.uri.scheme === Schemas.vscodeNotebookCell || this.editor.getModel()?.uri.scheme === Schemas.vscodeNotebookCell)) {
// if (this.editor.hasWidgetFocus()) {
// ! WordHighlighter.query?.model.dispose();
WordHighlighter.query = null;
}
// return if no model
if (!this.editor.hasModel()) {
return;
}
const currentDecorationIDs = WordHighlighter.storedDecorations.get(this.editor.getModel().uri);
if (!currentDecorationIDs) {
return;
}
this.editor.removeDecorations(currentDecorationIDs);
WordHighlighter.storedDecorations.delete(this.editor.getModel().uri);
if (this.decorations.length > 0) {
// remove decorations
this.decorations.clear();
this._hasWordHighlights.set(false);
}
}
private _removeAllDecorations(): void {
const currentEditors = this.codeEditorService.listCodeEditors();
// iterate over editors and store models in currentModels
for (const editor of currentEditors) {
if (!editor.hasModel()) {
continue;
}
const currentDecorationIDs = WordHighlighter.storedDecorations.get(editor.getModel().uri);
if (!currentDecorationIDs) {
continue;
}
editor.removeDecorations(currentDecorationIDs);
WordHighlighter.storedDecorations.delete(editor.getModel().uri);
const editorHighlighterContrib = WordHighlighterContribution.get(editor);
if (!editorHighlighterContrib?.wordHighlighter) {
continue;
}
if (editorHighlighterContrib.wordHighlighter.decorations.length > 0) {
editorHighlighterContrib.wordHighlighter.decorations.clear();
editorHighlighterContrib.wordHighlighter._hasWordHighlights.set(false);
}
}
}
private _stopSingular(): void {
// Remove any existing decorations + a possible query, and re - run to update decorations
this._removeSingleDecorations();
// don't re-run notebooks
if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) {
this._run();
}
// Cancel any renderDecorationsTimer
if (this.renderDecorationsTimer !== -1) {
clearTimeout(this.renderDecorationsTimer);
this.renderDecorationsTimer = -1;
}
// Cancel any worker request
if (this.workerRequest !== null) {
this.workerRequest.cancel();
this.workerRequest = null;
}
// Invalidate any worker request callback
if (!this.workerRequestCompleted) {
this.workerRequestTokenId++;
this.workerRequestCompleted = true;
}
}
private _stopAll(): void {
// Remove any existing decorations
this._removeDecorations();
this._removeAllDecorations();
// Cancel any renderDecorationsTimer
if (this.renderDecorationsTimer !== -1) {
@@ -342,7 +511,8 @@ class WordHighlighter {
}
// ignore typing & other
if (e.reason !== CursorChangeReason.Explicit) {
// need to check if the model is a notebook cell, should not stop if nb
if (e.reason !== CursorChangeReason.Explicit && this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) {
this._stopAll();
return;
}
@@ -361,33 +531,106 @@ class WordHighlighter {
});
}
private getOtherModelsToHighlight(model: ITextModel): ITextModel[] {
if (!model) {
return [];
}
// notebook case
const isNotebookEditor = model.uri.scheme === Schemas.vscodeNotebookCell;
if (isNotebookEditor) {
const currentModels: ITextModel[] = [];
const currentEditors = this.codeEditorService.listCodeEditors();
for (const editor of currentEditors) {
const tempModel = editor.getModel();
if (tempModel && tempModel !== model && tempModel.uri.scheme === Schemas.vscodeNotebookCell) {
currentModels.push(tempModel);
}
}
return currentModels;
}
// inline case
// ? current works when highlighting outside of an inline diff, highlighting in.
// ? broken when highlighting within a diff editor. highlighting the main editor does not work
// ? editor group service could be useful here
const currentModels: ITextModel[] = [];
const currentEditors = this.codeEditorService.listCodeEditors();
for (const editor of currentEditors) {
if (!isDiffEditor(editor)) {
continue;
}
const diffModel = (editor as IDiffEditor).getModel();
if (!diffModel) {
continue;
}
if (model === diffModel.modified) { // embedded inline chat diff would pass this, allowing highlights
//? currentModels.push(diffModel.original);
currentModels.push(diffModel.modified);
}
}
if (currentModels.length) { // no matching editors have been found
return currentModels;
}
// multi-doc OFF
if (!this.multiDocumentOccurrencesHighlight) {
return [];
}
// multi-doc ON
for (const editor of currentEditors) {
const tempModel = editor.getModel();
if (tempModel && tempModel !== model) {
currentModels.push(tempModel);
}
}
return currentModels;
}
private _run(): void {
const editorSelection = this.editor.getSelection();
// ignore multiline selection
if (editorSelection.startLineNumber !== editorSelection.endLineNumber) {
this._stopAll();
return;
let workerRequestIsValid;
if (!this.editor.hasWidgetFocus()) { // no focus (new nb cell, etc)
if (WordHighlighter.query === null) {
// no previous query, nothing to highlight
this._stopAll();
return;
}
} else {
const editorSelection = this.editor.getSelection();
// ignore multiline selection
if (!editorSelection || editorSelection.startLineNumber !== editorSelection.endLineNumber) {
this._stopAll();
return;
}
const startColumn = editorSelection.startColumn;
const endColumn = editorSelection.endColumn;
const word = this._getWord();
// The selection must be inside a word or surround one word at most
if (!word || word.startColumn > startColumn || word.endColumn < endColumn) {
// no previous query, nothing to highlight
this._stopAll();
return;
}
// All the effort below is trying to achieve this:
// - when cursor is moved to a word, trigger immediately a findOccurrences request
// - 250ms later after the last cursor move event, render the occurrences
// - no flickering!
workerRequestIsValid = (this.workerRequest && this.workerRequest.isValid(this.model, editorSelection, this.decorations));
WordHighlighter.query = {
model: this.model,
selection: editorSelection,
word: word
};
}
const startColumn = editorSelection.startColumn;
const endColumn = editorSelection.endColumn;
const word = this._getWord();
// The selection must be inside a word or surround one word at most
if (!word || word.startColumn > startColumn || word.endColumn < endColumn) {
this._stopAll();
return;
}
// All the effort below is trying to achieve this:
// - when cursor is moved to a word, trigger immediately a findOccurrences request
// - 250ms later after the last cursor move event, render the occurrences
// - no flickering!
const workerRequestIsValid = (this.workerRequest && this.workerRequest.isValid(this.model, editorSelection, this.decorations));
// There are 4 cases:
// a) old workerRequest is valid & completed, renderDecorationsTimer fired
// b) old workerRequest is valid & completed, renderDecorationsTimer not fired
@@ -415,7 +658,13 @@ class WordHighlighter {
const myRequestId = ++this.workerRequestTokenId;
this.workerRequestCompleted = false;
this.workerRequest = computeOccurencesAtPosition(this.providers, this.model, this.editor.getSelection(), this.editor.getOption(EditorOption.wordSeparators));
const otherModelsToHighlight = this.getOtherModelsToHighlight(this.editor.getModel());
if (!otherModelsToHighlight.length) {
this.workerRequest = computeOccurencesAtPosition(this.providers, WordHighlighter.query.model, WordHighlighter.query.selection, this.editor.getOption(EditorOption.wordSeparators));
} else {
this.workerRequest = computeOccurencesMultiModel(this.multiDocumentProviders, WordHighlighter.query.model, WordHighlighter.query.selection, this.editor.getOption(EditorOption.wordSeparators), otherModelsToHighlight);
}
this.workerRequest.result.then(data => {
if (myRequestId === this.workerRequestTokenId) {
@@ -445,31 +694,45 @@ class WordHighlighter {
private renderDecorations(): void {
this.renderDecorationsTimer = -1;
const decorations: IModelDeltaDecoration[] = [];
for (const info of this.workerRequestValue) {
if (info.range) {
decorations.push({
range: info.range,
options: getHighlightDecorationOptions(info.kind)
});
// create new loop, iterate over current editors using this.codeEditorService.listCodeEditors(),
// if the URI of that codeEditor is in the map, then add the decorations to the decorations array
// then set the decorations for the editor
const currentEditors = this.codeEditorService.listCodeEditors();
for (const editor of currentEditors) {
const editorHighlighterContrib = WordHighlighterContribution.get(editor);
if (!editorHighlighterContrib) {
continue;
}
}
this.decorations.set(decorations);
this._hasWordHighlights.set(this.hasDecorations());
const newDecorations: IModelDeltaDecoration[] = [];
const uri = editor.getModel()?.uri;
if (uri && this.workerRequestValue.has(uri)) {
const oldDecorationIDs: string[] | undefined = WordHighlighter.storedDecorations.get(uri);
const newDocumentHighlights = this.workerRequestValue.get(uri);
if (newDocumentHighlights) {
for (const highlight of newDocumentHighlights) {
newDecorations.push({
range: highlight.range,
options: getHighlightDecorationOptions(highlight.kind)
});
}
}
// update decorators of friends
for (const other of this.linkedHighlighters()) {
if (other?.editor.getModel() === this.editor.getModel()) {
other._stopAll();
other.decorations.set(decorations);
other._hasWordHighlights.set(other.hasDecorations());
let newDecorationIDs: string[] = [];
editor.changeDecorations((changeAccessor) => {
newDecorationIDs = changeAccessor.deltaDecorations(oldDecorationIDs ?? [], newDecorations);
});
WordHighlighter.storedDecorations = WordHighlighter.storedDecorations.set(uri, newDecorationIDs);
if (newDecorations.length > 0) {
editorHighlighterContrib.wordHighlighter?._hasWordHighlights.set(true);
}
}
}
}
public dispose(): void {
this._stopAll();
this._stopSingular();
this.toUnhook.dispose();
}
}
@@ -482,70 +745,59 @@ export class WordHighlighterContribution extends Disposable implements IEditorCo
return editor.getContribution<WordHighlighterContribution>(WordHighlighterContribution.ID);
}
private wordHighlighter: WordHighlighter | null;
private linkedContributions: Set<WordHighlighterContribution>;
private _wordHighlighter: WordHighlighter | null;
constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService) {
constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, @ICodeEditorService codeEditorService: ICodeEditorService) {
super();
this.wordHighlighter = null;
this.linkedContributions = new Set();
this._wordHighlighter = null;
const createWordHighlighterIfPossible = () => {
if (editor.hasModel() && !editor.getModel().isTooLargeForTokenization()) {
this.wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, () => Iterable.map(this.linkedContributions, c => c.wordHighlighter), contextKeyService);
this._wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, languageFeaturesService.multiDocumentHighlightProvider, contextKeyService, codeEditorService);
}
};
this._register(editor.onDidChangeModel((e) => {
if (this.wordHighlighter) {
this.wordHighlighter.dispose();
this.wordHighlighter = null;
if (this._wordHighlighter) {
this._wordHighlighter.dispose();
this._wordHighlighter = null;
}
createWordHighlighterIfPossible();
}));
createWordHighlighterIfPossible();
}
public get wordHighlighter(): WordHighlighter | null {
return this._wordHighlighter;
}
public saveViewState(): boolean {
if (this.wordHighlighter && this.wordHighlighter.hasDecorations()) {
if (this._wordHighlighter && this._wordHighlighter.hasDecorations()) {
return true;
}
return false;
}
public moveNext() {
this.wordHighlighter?.moveNext();
this._wordHighlighter?.moveNext();
}
public moveBack() {
this.wordHighlighter?.moveBack();
this._wordHighlighter?.moveBack();
}
public restoreViewState(state: boolean | undefined): void {
if (this.wordHighlighter && state) {
this.wordHighlighter.restore();
if (this._wordHighlighter && state) {
this._wordHighlighter.restore();
}
}
public stopHighlighting() {
this.wordHighlighter?.stop();
}
public linkWordHighlighters(editor: ICodeEditor): IDisposable {
const other = WordHighlighterContribution.get(editor);
if (!other) {
return Disposable.None;
}
this.linkedContributions.add(other);
other.linkedContributions.add(this);
return toDisposable(() => {
this.linkedContributions.delete(other);
other.linkedContributions.delete(this);
});
this._wordHighlighter?.stop();
}
public override dispose(): void {
if (this.wordHighlighter) {
this.wordHighlighter.dispose();
this.wordHighlighter = null;
if (this._wordHighlighter) {
this._wordHighlighter.dispose();
this._wordHighlighter = null;
}
super.dispose();
}
+86 -68
View File
@@ -3531,6 +3531,11 @@ declare namespace monaco.editor {
* Defaults to true.
*/
occurrencesHighlight?: boolean;
/**
* Enable semantic occurrences highlight.
* Defaults to true.
*/
multiDocumentOccurrencesHighlight?: boolean;
/**
* Show code lens
* Defaults to true.
@@ -4786,74 +4791,75 @@ declare namespace monaco.editor {
multiCursorModifier = 77,
multiCursorPaste = 78,
multiCursorLimit = 79,
occurrencesHighlight = 80,
overviewRulerBorder = 81,
overviewRulerLanes = 82,
padding = 83,
pasteAs = 84,
parameterHints = 85,
peekWidgetDefaultFocus = 86,
definitionLinkOpensInPeek = 87,
quickSuggestions = 88,
quickSuggestionsDelay = 89,
readOnly = 90,
readOnlyMessage = 91,
renameOnType = 92,
renderControlCharacters = 93,
renderFinalNewline = 94,
renderLineHighlight = 95,
renderLineHighlightOnlyWhenFocus = 96,
renderValidationDecorations = 97,
renderWhitespace = 98,
revealHorizontalRightPadding = 99,
roundedSelection = 100,
rulers = 101,
scrollbar = 102,
scrollBeyondLastColumn = 103,
scrollBeyondLastLine = 104,
scrollPredominantAxis = 105,
selectionClipboard = 106,
selectionHighlight = 107,
selectOnLineNumbers = 108,
showFoldingControls = 109,
showUnused = 110,
snippetSuggestions = 111,
smartSelect = 112,
smoothScrolling = 113,
stickyScroll = 114,
stickyTabStops = 115,
stopRenderingLineAfter = 116,
suggest = 117,
suggestFontSize = 118,
suggestLineHeight = 119,
suggestOnTriggerCharacters = 120,
suggestSelection = 121,
tabCompletion = 122,
tabIndex = 123,
unicodeHighlighting = 124,
unusualLineTerminators = 125,
useShadowDOM = 126,
useTabStops = 127,
wordBreak = 128,
wordSeparators = 129,
wordWrap = 130,
wordWrapBreakAfterCharacters = 131,
wordWrapBreakBeforeCharacters = 132,
wordWrapColumn = 133,
wordWrapOverride1 = 134,
wordWrapOverride2 = 135,
wrappingIndent = 136,
wrappingStrategy = 137,
showDeprecated = 138,
inlayHints = 139,
editorClassName = 140,
pixelRatio = 141,
tabFocusMode = 142,
layoutInfo = 143,
wrappingInfo = 144,
defaultColorDecorators = 145,
colorDecoratorsActivatedOn = 146,
inlineCompletionsAccessibilityVerbose = 147
multiDocumentOccurrencesHighlight = 80,
occurrencesHighlight = 81,
overviewRulerBorder = 82,
overviewRulerLanes = 83,
padding = 84,
pasteAs = 85,
parameterHints = 86,
peekWidgetDefaultFocus = 87,
definitionLinkOpensInPeek = 88,
quickSuggestions = 89,
quickSuggestionsDelay = 90,
readOnly = 91,
readOnlyMessage = 92,
renameOnType = 93,
renderControlCharacters = 94,
renderFinalNewline = 95,
renderLineHighlight = 96,
renderLineHighlightOnlyWhenFocus = 97,
renderValidationDecorations = 98,
renderWhitespace = 99,
revealHorizontalRightPadding = 100,
roundedSelection = 101,
rulers = 102,
scrollbar = 103,
scrollBeyondLastColumn = 104,
scrollBeyondLastLine = 105,
scrollPredominantAxis = 106,
selectionClipboard = 107,
selectionHighlight = 108,
selectOnLineNumbers = 109,
showFoldingControls = 110,
showUnused = 111,
snippetSuggestions = 112,
smartSelect = 113,
smoothScrolling = 114,
stickyScroll = 115,
stickyTabStops = 116,
stopRenderingLineAfter = 117,
suggest = 118,
suggestFontSize = 119,
suggestLineHeight = 120,
suggestOnTriggerCharacters = 121,
suggestSelection = 122,
tabCompletion = 123,
tabIndex = 124,
unicodeHighlighting = 125,
unusualLineTerminators = 126,
useShadowDOM = 127,
useTabStops = 128,
wordBreak = 129,
wordSeparators = 130,
wordWrap = 131,
wordWrapBreakAfterCharacters = 132,
wordWrapBreakBeforeCharacters = 133,
wordWrapColumn = 134,
wordWrapOverride1 = 135,
wordWrapOverride2 = 136,
wrappingIndent = 137,
wrappingStrategy = 138,
showDeprecated = 139,
inlayHints = 140,
editorClassName = 141,
pixelRatio = 142,
tabFocusMode = 143,
layoutInfo = 144,
wrappingInfo = 145,
defaultColorDecorators = 146,
colorDecoratorsActivatedOn = 147,
inlineCompletionsAccessibilityVerbose = 148
}
export const EditorOptions: {
@@ -4940,6 +4946,7 @@ declare namespace monaco.editor {
multiCursorPaste: IEditorOption<EditorOption.multiCursorPaste, 'spread' | 'full'>;
multiCursorLimit: IEditorOption<EditorOption.multiCursorLimit, number>;
occurrencesHighlight: IEditorOption<EditorOption.occurrencesHighlight, boolean>;
multiDocumentOccurrencesHighlight: IEditorOption<EditorOption.multiDocumentOccurrencesHighlight, boolean>;
overviewRulerBorder: IEditorOption<EditorOption.overviewRulerBorder, boolean>;
overviewRulerLanes: IEditorOption<EditorOption.overviewRulerLanes, number>;
padding: IEditorOption<EditorOption.padding, Readonly<Required<IEditorPaddingOptions>>>;
@@ -7146,6 +7153,17 @@ declare namespace monaco.languages {
provideDocumentHighlights(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<DocumentHighlight[]>;
}
export interface MultiDocumentHighlightProvider {
/**
* Provide a Map of Uri --> document highlights, like all occurrences of a variable or
* all exit-points of a function.
*
* Used in cases such as split view, notebooks, etc. where there can be multiple documents
* with shared symbols.
*/
provideMultiDocumentHighlights(primaryModel: editor.ITextModel, position: Position, otherModels: editor.ITextModel[], token: CancellationToken): ProviderResult<Map<Uri, DocumentHighlight[]>>;
}
/**
* The linked editing range provider interface defines the contract between extensions and
* the linked editing feature.
@@ -33,6 +33,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers';
import { ExtHostContext, ExtHostLanguageFeaturesShape, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentDropEditProviderMetadata, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol';
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/core/wordHelper';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
@@ -293,11 +294,51 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
// --- occurrences
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, this._languageFeaturesService.documentHighlightProvider.register(selector, <languages.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<languages.DocumentHighlight[] | undefined> => {
return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
}
}));
this._registrations.set(handle, combinedDisposable(
this._languageFeaturesService.documentHighlightProvider.register(selector, <languages.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<languages.DocumentHighlight[] | undefined> => {
return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
}
}),
this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, <languages.MultiDocumentHighlightProvider>{
provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise<Map<URI, languages.DocumentHighlight[]>> => {
const res = this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
if (!res) {
return Promise.resolve(new Map<URI, languages.DocumentHighlight[]>());
}
return res.then(data => {
const result = new Map<URI, languages.DocumentHighlight[]>();
if (data) {
result.set(model.uri, data);
}
const word = model.getWordAtPosition({
lineNumber: position.lineNumber,
column: position.column
});
if (!word) {
return new Map<URI, languages.DocumentHighlight[]>();
}
for (const otherModel of otherModels) {
const matches = otherModel.findMatches(word.word, true, false, true, USUAL_WORD_SEPARATORS, false);
const highlights = matches.map(m => ({
range: m.range,
kind: languages.DocumentHighlightKind.Text
}));
if (highlights) {
result.set(otherModel.uri, highlights);
}
}
return result;
});
}
})
));
}
// --- linked editing
@@ -508,8 +508,8 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
assert.strictEqual(value.length, 1);
const [entry] = value;
assert.strictEqual(value.size, 1);
const [entry] = Array.from(value.values())[0];
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
});
@@ -529,8 +529,8 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
assert.strictEqual(value.length, 1);
const [entry] = value;
assert.strictEqual(value.size, 1);
const [entry] = Array.from(value.values())[0];
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 });
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
});
@@ -550,8 +550,8 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = (await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None))!;
assert.strictEqual(value.length, 1);
const [entry] = value;
assert.strictEqual(value.size, 1);
const [entry] = Array.from(value.values())[0];
assert.deepStrictEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 });
assert.strictEqual(entry.kind, languages.DocumentHighlightKind.Text);
});
@@ -572,7 +572,7 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = await getOccurrencesAtPosition(languageFeaturesService.documentHighlightProvider, model, new EditorPosition(1, 2), CancellationToken.None);
assert.strictEqual(value!.length, 1);
assert.strictEqual(value!.size, 1);
});
// --- references
@@ -33,7 +33,6 @@ import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
import { WordHighlighterContribution } from 'vs/editor/contrib/wordHighlighter/browser/wordHighlighter';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { generateUuid } from 'vs/base/common/uuid';
@@ -102,10 +101,6 @@ export class InlineChatLivePreviewWidget extends ZoneWidget {
this._disposables.add(this._diffEditor.onDidUpdateDiff(() => { onDidChangeDiff(); }));
}
const highlighter = WordHighlighterContribution.get(editor);
if (highlighter) {
this._disposables.add(highlighter.linkWordHighlighters(this._diffEditor.getModifiedEditor()));
}
const doStyle = () => {
const theme = themeService.getColorTheme();