diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index e9fc22f531c4e..60c9692b17a04 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -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. @@ -5102,6 +5107,7 @@ export const enum EditorOption { multiCursorModifier, multiCursorPaste, multiCursorLimit, + multiDocumentOccurrencesHighlight, occurrencesHighlight, overviewRulerBorder, overviewRulerLanes, @@ -5602,6 +5608,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.") } diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 7c9a7a5a308f4..a9bbdd6b19e36 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -965,6 +965,17 @@ export interface DocumentHighlightProvider { provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +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>; +} + /** * The linked editing range provider interface defines the contract between extensions and * the linked editing feature. diff --git a/src/vs/editor/common/services/languageFeatures.ts b/src/vs/editor/common/services/languageFeatures.ts index df53beaecdb0d..760e6e6e22adf 100644 --- a/src/vs/editor/common/services/languageFeatures.ts +++ b/src/vs/editor/common/services/languageFeatures.ts @@ -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'); @@ -49,6 +49,8 @@ export interface ILanguageFeaturesService { readonly documentHighlightProvider: LanguageFeatureRegistry; + readonly multiDocumentHighlightProvider: LanguageFeatureRegistry; + readonly documentRangeSemanticTokensProvider: LanguageFeatureRegistry; readonly documentSemanticTokensProvider: LanguageFeatureRegistry; diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/services/languageFeaturesService.ts index 3ef13891fb76a..c6504614ce396 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/services/languageFeaturesService.ts @@ -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(this._score.bind(this)); readonly hoverProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly documentHighlightProvider = new LanguageFeatureRegistry(this._score.bind(this)); + readonly multiDocumentHighlightProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly selectionRangeProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly foldingRangeProvider = new LanguageFeatureRegistry(this._score.bind(this)); readonly linkProvider = new LanguageFeatureRegistry(this._score.bind(this)); diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 622f4e56f23ee..765df7b76d6e7 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -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 } /** diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 943bad34e8098..c491bdb8d7bc1 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -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('hasWordHighlights', false); -export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, position: Position, token: CancellationToken): Promise { - +export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, position: Position, token: CancellationToken): Promise | null | undefined> { const orderedByScore = registry.ordered(model); // in order of score ask the occurrences provider @@ -43,19 +44,44 @@ export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry(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(); + map.set(model.uri, result); + return map; + } + return new ResourceMap(); + }); +} + +export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegistry, model: ITextModel, position: Position, wordSeparators: string, token: CancellationToken, otherModels: ITextModel[]): Promise | 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 | null | undefined>(orderedByScore.map(provider => () => { + return Promise.resolve(provider.provideMultiDocumentHighlights(model, position, otherModels, token)) + .then(undefined, onUnexpectedExternalError); + }), (t: ResourceMap | null | undefined): t is ResourceMap => t instanceof Map && t.size > 0); } interface IOccurenceAtPositionRequest { - readonly result: Promise; + readonly result: Promise>; 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 | null; + private _result: CancelablePromise> | 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; + protected abstract _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise>; 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 { - return getOccurrencesAtPosition(this._providers, model, selection.getPosition(), token).then(value => value || []); + protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { + return getOccurrencesAtPosition(this._providers, model, selection.getPosition(), token).then(value => { + if (!value) { + return new ResourceMap(); + } + return value; + }); + } +} + +class MultiModelOccurenceRequest extends OccurenceAtPositionRequest { + private readonly _providers: LanguageFeatureRegistry; + private readonly _otherModels: ITextModel[]; + + constructor(model: ITextModel, selection: Selection, wordSeparators: string, providers: LanguageFeatureRegistry, otherModels: ITextModel[]) { + super(model, selection, wordSeparators); + this._providers = providers; + this._otherModels = otherModels; + } + + protected override _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { + return getOccurrencesAcrossMultipleModels(this._providers, model, selection.getPosition(), wordSeparators, token, this._otherModels).then(value => { + if (!value) { + return new ResourceMap(); + } + return value; + }); } } -class TextualOccurenceAtPositionRequest extends OccurenceAtPositionRequest { +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 { + protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { return timeout(250, token).then(() => { - if (!selection.isEmpty()) { - return []; + const result = new ResourceMap(); + const word = model.getWordAtPosition(selection.getPosition()); + if (!word) { + return new ResourceMap(); } - const word = model.getWordAtPosition(selection.getPosition()); + const allModels = [model, ...this._otherModels]; - if (!word || word.word.length > 1000) { - return []; - } - const matches = model.findMatches(word.word, true, false, true, wordSeparators, false); - return matches.map(m => { - return { + 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, 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; + private readonly multiDocumentProviders: LanguageFeatureRegistry; 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 = new ResourceMap(); private lastCursorPositionChangeTime: number = 0; private renderDecorationsTimer: any = -1; @@ -193,18 +264,20 @@ class WordHighlighter { private readonly _hasWordHighlights: IContextKey; private _ignorePositionChangeEvent: boolean; - private readonly linkedHighlighters: () => Iterable; + private static storedDecorations: ResourceMap = new ResourceMap(); + private static query: IWordHighlighterQuery | null = null; - constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry, linkedHighlighters: () => Iterable, contextKeyService: IContextKeyService) { + constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry, multiProviders: LanguageFeatureRegistry, 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,32 +531,105 @@ class WordHighlighter { }); } - private _run(): void { - const editorSelection = this.editor.getSelection(); + private getOtherModelsToHighlight(model: ITextModel): ITextModel[] { + if (!model) { + return []; + } - // ignore multiline selection - if (editorSelection.startLineNumber !== editorSelection.endLineNumber) { - this._stopAll(); - 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; } - const startColumn = editorSelection.startColumn; - const endColumn = editorSelection.endColumn; + // 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; + } - const word = this._getWord(); + // multi-doc OFF + if (!this.multiDocumentOccurrencesHighlight) { + return []; + } - // The selection must be inside a word or surround one word at most - if (!word || word.startColumn > startColumn || word.endColumn < endColumn) { - this._stopAll(); - return; + // multi-doc ON + for (const editor of currentEditors) { + const tempModel = editor.getModel(); + if (tempModel && tempModel !== model) { + currentModels.push(tempModel); + } } + return currentModels; + } - // 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! + private _run(): void { - const workerRequestIsValid = (this.workerRequest && this.workerRequest.isValid(this.model, editorSelection, this.decorations)); + 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 + }; + } // There are 4 cases: // a) old workerRequest is valid & completed, renderDecorationsTimer 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) + }); + } + } + + let newDecorationIDs: string[] = []; + editor.changeDecorations((changeAccessor) => { + newDecorationIDs = changeAccessor.deltaDecorations(oldDecorationIDs ?? [], newDecorations); + }); + WordHighlighter.storedDecorations = WordHighlighter.storedDecorations.set(uri, newDecorationIDs); - // 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()); + 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.ID); } - private wordHighlighter: WordHighlighter | null; - private linkedContributions: Set; + 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(); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 1a29648bee0ce..91b1c4f2667fa 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -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. @@ -4780,74 +4785,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: { @@ -4934,6 +4940,7 @@ declare namespace monaco.editor { multiCursorPaste: IEditorOption; multiCursorLimit: IEditorOption; occurrencesHighlight: IEditorOption; + multiDocumentOccurrencesHighlight: IEditorOption; overviewRulerBorder: IEditorOption; overviewRulerLanes: IEditorOption; padding: IEditorOption>>; @@ -7140,6 +7147,17 @@ declare namespace monaco.languages { provideDocumentHighlights(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } + 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>; + } + /** * The linked editing range provider interface defines the contract between extensions and * the linked editing feature. diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index fb79323e683c0..0b718ddd425f4 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -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, { - provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { - return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token); - } - })); + this._registrations.set(handle, combinedDisposable( + this._languageFeaturesService.documentHighlightProvider.register(selector, { + provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { + return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token); + } + }), + this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, { + provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise> => { + const res = this._proxy.$provideDocumentHighlights(handle, model.uri, position, token); + if (!res) { + return Promise.resolve(new Map()); + } + + return res.then(data => { + const result = new Map(); + if (data) { + result.set(model.uri, data); + } + + const word = model.getWordAtPosition({ + lineNumber: position.lineNumber, + column: position.column + }); + + if (!word) { + return new Map(); + } + + 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 diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 4764659511340..a04dc9da18ec8 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -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 diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 12dbe5db4810c..a906bd23d24c4 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -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();