Add multi-document highlight provider feature (#198467)

* multi-doc api + text provider editor feature + extHost hookup + typescript semantic multi-doc

* fix disposable leak, wasn't setting providers to the disposableMap

* filter unnecessary models to fix errors with typescript provider

* fix nits (todo - doc filter)

* fix typo from merge conflict

* expose LanguageSelector in multi-doc provider, filter out 0 score models
This commit is contained in:
Michael Lively
2023-11-22 10:07:20 -08:00
committed by GitHub
parent ab4f71b394
commit e33219dde3
15 changed files with 339 additions and 57 deletions
@@ -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, <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 word = model.getWordAtPosition({
lineNumber: position.lineNumber,
column: position.column
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);
}
}));
}
$registerMultiDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, <languages.MultiDocumentHighlightProvider>{
selector: selector,
provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise<Map<URI, languages.DocumentHighlight[]> | 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<languages.DocumentHighlight[]>();
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<URI, languages.DocumentHighlight[]>();
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