diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 630e8074610..880c8dc462b 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -8,8 +8,9 @@ "license": "MIT", "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "enabledApiProposals": [ - "mappedEditsProvider", - "workspaceTrust" + "workspaceTrust", + "multiDocumentHighlightProvider", + "mappedEditsProvider" ], "capabilities": { "virtualWorkspaces": { diff --git a/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts b/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts index b3489ecdbc5..2213e3b4ee2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts +++ b/extensions/typescript-language-features/src/languageFeatures/documentHighlight.ts @@ -9,11 +9,43 @@ import type * as Proto from '../tsServer/protocol/protocol'; import * as typeConverters from '../typeConverters'; import { ITypeScriptServiceClient } from '../typescriptService'; -class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider { +class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider, vscode.MultiDocumentHighlightProvider { public constructor( private readonly client: ITypeScriptServiceClient ) { } + public async provideMultiDocumentHighlights( + document: vscode.TextDocument, + position: vscode.Position, + otherDocuments: vscode.TextDocument[], + token: vscode.CancellationToken + ): Promise { + const allFiles = [document, ...otherDocuments].map(doc => this.client.toOpenTsFilePath(doc)).filter(file => !!file) as string[]; + const file = this.client.toOpenTsFilePath(document); + + if (!file || allFiles.length === 0) { + return []; + } + + const args = { + ...typeConverters.Position.toFileLocationRequestArgs(file, position), + filesToSearch: allFiles + }; + const response = await this.client.execute('documentHighlights', args, token); + if (response.type !== 'response' || !response.body) { + return []; + } + + const result = response.body.map(highlightItem => + new vscode.MultiDocumentHighlight( + vscode.Uri.file(highlightItem.file), + [...convertDocumentHighlight(highlightItem)] + ) + ); + + return result; + } + public async provideDocumentHighlights( document: vscode.TextDocument, position: vscode.Position, @@ -48,6 +80,10 @@ export function register( selector: DocumentSelector, client: ITypeScriptServiceClient, ) { - return vscode.languages.registerDocumentHighlightProvider(selector.syntax, - new TypeScriptDocumentHighlightProvider(client)); + const provider = new TypeScriptDocumentHighlightProvider(client); + + return vscode.Disposable.from( + vscode.languages.registerDocumentHighlightProvider(selector.syntax, provider), + vscode.languages.registerMultiDocumentHighlightProvider(selector.syntax, provider) + ); } diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 2a4a57847d4..f5236f44031 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -11,7 +11,8 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", + "../../src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts", "../../src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts", - "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts" ] } diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 1489bb842ba..a836db5afad 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -24,6 +24,7 @@ import { ContiguousMultilineTokens } from 'vs/editor/common/tokens/contiguousMul import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IMarkerData } from 'vs/platform/markers/common/markers'; +import { LanguageFilter } from 'vs/editor/common/languageSelector'; /** * @internal @@ -953,6 +954,22 @@ export interface DocumentHighlight { */ kind?: DocumentHighlightKind; } + +/** + * Represents a set of document highlights for a specific URI. + */ +export interface MultiDocumentHighlight { + /** + * The URI of the document that the highlights belong to. + */ + uri: URI; + + /** + * The set of highlights for the document. + */ + highlights: DocumentHighlight[]; +} + /** * The document highlight provider interface defines the contract between extensions and * the word-highlight-feature. @@ -965,13 +982,24 @@ export interface DocumentHighlightProvider { provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * A provider that can provide document highlights across multiple documents. + */ export interface MultiDocumentHighlightProvider { + selector: LanguageFilter; + /** * 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. + * + * @param primaryModel The primary text model. + * @param position The position at which to provide document highlights. + * @param otherModels The other text models to search for document highlights. + * @param token A cancellation token. + * @returns A map of URI to document highlights. */ provideMultiDocumentHighlights(primaryModel: model.ITextModel, position: Position, otherModels: model.ITextModel[], token: CancellationToken): ProviderResult>; } diff --git a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts new file mode 100644 index 00000000000..1d7a65695d3 --- /dev/null +++ b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/core/wordHelper'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { DocumentHighlight, DocumentHighlightKind, MultiDocumentHighlightProvider, ProviderResult } from 'vs/editor/common/languages'; +import { ITextModel } from 'vs/editor/common/model'; +import { Position } from 'vs/editor/common/core/position'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; +import { LanguageFilter } from 'vs/editor/common/languageSelector'; + + +class TextualDocumentHighlightProvider implements MultiDocumentHighlightProvider { + + selector: LanguageFilter = { language: '*' }; + + provideMultiDocumentHighlights(primaryModel: ITextModel, position: Position, otherModels: ITextModel[], token: CancellationToken): ProviderResult> { + + const result = new ResourceMap(); + + const word = primaryModel.getWordAtPosition({ + lineNumber: position.lineNumber, + column: position.column + }); + if (!word) { + return Promise.resolve(result); + } + + + for (const model of [primaryModel, ...otherModels]) { + if (model.isDisposed()) { + continue; + } + + const matches = model.findMatches(word.word, true, false, true, USUAL_WORD_SEPARATORS, false); + const highlights = matches.map(m => ({ + range: m.range, + kind: DocumentHighlightKind.Text + })); + + if (highlights) { + result.set(model.uri, highlights); + } + } + + return result; + } + +} + +export class TextualMultiDocumentHighlightFeature extends Disposable { + constructor( + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + ) { + super(); + + this._register(languageFeaturesService.multiDocumentHighlightProvider.register('*', new TextualDocumentHighlightProvider())); + } +} diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 6bfb4c0e186..c1fc29d7d0b 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -32,6 +32,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Schemas } from 'vs/base/common/network'; import { ResourceMap } from 'vs/base/common/map'; +import { score } from 'vs/editor/common/languageSelector'; +// import { TextualMultiDocumentHighlightFeature } from 'vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider'; +// import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); @@ -61,9 +64,13 @@ export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegi // 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)) + const filteredModels = otherModels.filter(otherModel => { + return score(provider.selector, otherModel.uri, otherModel.getLanguageId(), true, undefined, undefined) > 0; + }); + + return Promise.resolve(provider.provideMultiDocumentHighlights(model, position, filteredModels, token)) .then(undefined, onUnexpectedExternalError); - }), (t: ResourceMap | null | undefined): t is ResourceMap => t instanceof Map && t.size > 0); + }), (t: ResourceMap | null | undefined): t is ResourceMap => t instanceof ResourceMap && t.size > 0); } interface IOccurenceAtPositionRequest { @@ -585,7 +592,10 @@ class WordHighlighter { // multi-doc ON for (const editor of currentEditors) { const tempModel = editor.getModel(); - if (tempModel && tempModel !== model) { + + const isValidModel = tempModel && tempModel !== model; + + if (isValidModel) { currentModels.push(tempModel); } } @@ -910,3 +920,4 @@ registerEditorContribution(WordHighlighterContribution.ID, WordHighlighterContri registerEditorAction(NextWordHighlightAction); registerEditorAction(PrevWordHighlightAction); registerEditorAction(TriggerWordHighlightAction); +// registerEditorFeature(TextualMultiDocumentHighlightFeature); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5e9c9552b4d..8acccefe382 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7158,6 +7158,20 @@ declare namespace monaco.languages { kind?: DocumentHighlightKind; } + /** + * Represents a set of document highlights for a specific Uri. + */ + export interface MultiDocumentHighlight { + /** + * The Uri of the document that the highlights belong to. + */ + uri: Uri; + /** + * The set of highlights for the document. + */ + highlights: DocumentHighlight[]; + } + /** * The document highlight provider interface defines the contract between extensions and * the word-highlight-feature. @@ -7170,13 +7184,23 @@ declare namespace monaco.languages { provideDocumentHighlights(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } + /** + * A provider that can provide document highlights across multiple documents. + */ export interface MultiDocumentHighlightProvider { + selector: LanguageFilter; /** * 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. + * + * @param primaryModel The primary text model. + * @param position The position at which to provide document highlights. + * @param otherModels The other text models to search for document highlights. + * @param token A cancellation token. + * @returns A map of Uri to document highlights. */ provideMultiDocumentHighlights(primaryModel: editor.ITextModel, position: Position, otherModels: editor.ITextModel[], token: CancellationToken): ProviderResult>; } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 088ccde1823..a9fb937ed58 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -33,7 +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'; +import { ResourceMap } from 'vs/base/common/map'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; @extHostNamedCustomer(MainContext.MainThreadLanguageFeatures) @@ -295,52 +295,35 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- occurrences $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { - 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 word = model.getWordAtPosition({ - lineNumber: position.lineNumber, - column: position.column + 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); + } + })); + } + + $registerMultiDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { + this._registrations.set(handle, this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, { + selector: selector, + provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise | undefined> => { + return this._proxy.$provideMultiDocumentHighlights(handle, model.uri, position, otherModels.map(model => model.uri), token).then(dto => { + if (isFalsyOrEmpty(dto)) { + return undefined; + } + const result = new ResourceMap(); + dto?.forEach(value => { + // check if the URI exists already, if so, combine the highlights, otherwise create a new entry + const uri = URI.revive(value.uri); + if (result.has(uri)) { + result.get(uri)!.push(...value.highlights); + } else { + result.set(uri, value.highlights); + } }); - - const res = this._proxy.$provideDocumentHighlights(handle, model.uri, position, token); - return res.then(data => { - const result = new Map(); - if (isFalsyOrEmpty(data) || !data) { - return result; - } - result.set(model.uri, data); - - if (!word) { - return result; - } - - for (const otherModel of otherModels) { - if (otherModel.isDisposed()) { - continue; - } - - 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; - }); - } - }) - )); + return result; + }); + } + })); } // --- linked editing diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5d49e3763b8..3db9fda288d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -551,6 +551,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, + registerMultiDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.MultiDocumentHighlightProvider): vscode.Disposable { + return extHostLanguageFeatures.registerMultiDocumentHighlightProvider(extension, checkSelector(selector), provider); + }, registerLinkedEditingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { return extHostLanguageFeatures.registerLinkedEditingRangeProvider(extension, checkSelector(selector), provider); }, @@ -1460,6 +1463,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Disposable: extHostTypes.Disposable, DocumentHighlight: extHostTypes.DocumentHighlight, DocumentHighlightKind: extHostTypes.DocumentHighlightKind, + MultiDocumentHighlight: extHostTypes.MultiDocumentHighlight, DocumentLink: extHostTypes.DocumentLink, DocumentSymbol: extHostTypes.DocumentSymbol, EndOfLine: extHostTypes.EndOfLine, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b2e5fd71e51..bfdaa2bcade 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -405,6 +405,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void; $emitInlineValuesEvent(eventHandle: number, event?: any): void; $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; + $registerMultiDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void; $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto, displayName: string, supportsResolve: boolean): void; @@ -2063,6 +2064,7 @@ export interface ExtHostLanguageFeaturesShape { $provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideInlineValues(handle: number, resource: UriComponents, range: IRange, context: languages.InlineValueContext, token: CancellationToken): Promise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; + $provideMultiDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, otherModels: UriComponents[], token: CancellationToken): Promise | undefined>; $provideLinkedEditingRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: languages.ReferenceContext, token: CancellationToken): Promise; $provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: languages.CodeActionContext, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 36bd57ce591..9093f5a90fb 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -327,6 +327,27 @@ class DocumentHighlightAdapter { } } +class MultiDocumentHighlightAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.MultiDocumentHighlightProvider + ) { } + + async provideMultiDocumentHighlights(resource: URI, position: IPosition, otherResources: URI[], token: CancellationToken): Promise { + + const doc = this._documents.getDocument(resource); + const otherDocuments = otherResources.map(r => this._documents.getDocument(r)); + const pos = typeConvert.Position.to(position); + + const value = await this._provider.provideMultiDocumentHighlights(doc, pos, otherDocuments, token); + if (Array.isArray(value)) { + return value.map(typeConvert.MultiDocumentHighlight.from); + } + return undefined; + } +} + class LinkedEditingRangeAdapter { constructor( private readonly _documents: ExtHostDocuments, @@ -1853,8 +1874,9 @@ class MappedEditsAdapter { } type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter - | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentPasteEditProvider | DocumentFormattingAdapter - | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter + | DocumentHighlightAdapter | MultiDocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter + | DocumentPasteEditProvider | DocumentFormattingAdapter | RangeFormattingAdapter + | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | CompletionsAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter @@ -1890,6 +1912,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures); } + private _transformDocumentSelector(selector: vscode.DocumentSelector, extension: IExtensionDescription): Array { return typeConvert.DocumentSelector.from(selector, this._uriTransformer, extension); } @@ -2093,10 +2116,20 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF return this._createDisposable(handle); } + registerMultiDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.MultiDocumentHighlightProvider): vscode.Disposable { + const handle = this._addNewAdapter(new MultiDocumentHighlightAdapter(this._documents, provider), extension); + this._proxy.$registerMultiDocumentHighlightProvider(handle, this._transformDocumentSelector(selector, extension)); + return this._createDisposable(handle); + } + $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token), undefined, token); } + $provideMultiDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, otherModels: UriComponents[], token: CancellationToken): Promise { + return this._withAdapter(handle, MultiDocumentHighlightAdapter, adapter => adapter.provideMultiDocumentHighlights(URI.revive(resource), position, otherModels.map(model => URI.revive(model)), token), undefined, token); + } + // --- linked editing registerLinkedEditingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.LinkedEditingRangeProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b40cbc21368..c526eab3496 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1023,6 +1023,19 @@ export namespace DocumentHighlight { } } +export namespace MultiDocumentHighlight { + export function from(multiDocumentHighlight: vscode.MultiDocumentHighlight): languages.MultiDocumentHighlight { + return { + uri: multiDocumentHighlight.uri, + highlights: multiDocumentHighlight.highlights.map(DocumentHighlight.from) + }; + } + + export function to(multiDocumentHighlight: languages.MultiDocumentHighlight): types.MultiDocumentHighlight { + return new types.MultiDocumentHighlight(URI.revive(multiDocumentHighlight.uri), multiDocumentHighlight.highlights.map(DocumentHighlight.to)); + } +} + export namespace CompletionTriggerKind { export function to(kind: languages.CompletionTriggerKind) { switch (kind) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index c51fb2135c4..33ce8ca3274 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1227,6 +1227,25 @@ export class DocumentHighlight { } } +@es5ClassCompat +export class MultiDocumentHighlight { + + uri: URI; + highlights: DocumentHighlight[]; + + constructor(uri: URI, highlights: DocumentHighlight[]) { + this.uri = uri; + this.highlights = highlights; + } + + toJSON(): any { + return { + uri: this.uri, + highlights: this.highlights.map(h => h.toJSON()) + }; + } +} + export enum SymbolKind { File = 0, Module = 1, diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index e29607525e3..ac57eb9fb75 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -61,6 +61,7 @@ export const allApiProposals = Object.freeze({ ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', languageStatusText: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatusText.d.ts', mappedEditsProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', + multiDocumentHighlightProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookControllerAffinityHidden: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerAffinityHidden.d.ts', notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', diff --git a/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts b/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts new file mode 100644 index 00000000000..e03250799f0 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + /** + * Represents a collection of document highlights from multiple documents. + */ + export class MultiDocumentHighlight { + + /** + * The URI of the document containing the highlights. + */ + uri: Uri; + + /** + * The highlights for the document. + */ + highlights: DocumentHighlight[]; + + /** + * Creates a new instance of MultiDocumentHighlight. + * @param uri The URI of the document containing the highlights. + * @param highlights The highlights for the document. + */ + constructor(uri: Uri, highlights: DocumentHighlight[]); + } + + export interface MultiDocumentHighlightProvider { + + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param otherDocuments An array of additional valid documents for which highlights should be provided. + * @param token A cancellation token. + * @returns A Map containing a mapping of the Uri of a document to the document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty map. + */ + provideMultiDocumentHighlights(document: TextDocument, position: Position, otherDocuments: TextDocument[], token: CancellationToken): ProviderResult; + } + + namespace languages { + + /** + * Register a multi document highlight provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups sequentially asked for document highlights. + * The process stops when a provider returns a `non-falsy` or `non-failure` result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A multi-document highlight provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerMultiDocumentHighlightProvider(selector: DocumentSelector, provider: MultiDocumentHighlightProvider): Disposable; + } + +}