提交 8135848f 编写于 作者: Y Yan Zhang

add type hierarchy provider

Signed-off-by: NYan Zhang <yanzh@microsoft.com>
上级 d3abc86b
......@@ -2914,4 +2914,102 @@ declare module 'vscode' {
export type DetailedCoverage = StatementCoverage | FunctionCoverage;
//#endregion
//#region https://github.com/microsoft/vscode/issues/128444 --- Type hierarchy --- @eskibear
export class TypeHierarchyItem {
/**
* The name of this item.
*/
name: string;
/**
* The kind of this item.
*/
kind: SymbolKind;
/**
* Tags for this item.
*/
tags?: ReadonlyArray<SymbolTag>;
/**
* More detail for this item, e.g. the signature of a function.
*/
detail?: string;
/**
* The resource identifier of this item.
*/
uri: Uri;
/**
* The range enclosing this symbol not including leading/trailing whitespace
* but everything else, e.g. comments and code.
*/
range: Range;
/**
* The range that should be selected and revealed when this symbol is being
* picked, e.g. the name of a function. Must be contained by the
* [`range`](#TypeHierarchyItem.range).
*/
selectionRange: Range;
/**
* A data entry field that is preserved between a type hierarchy prepare and
* supertypes or subtypes requests. It could also be used to identify the
* type hierarchy in the server, helping improve the performance on
* resolving supertypes and subtypes.
*/
data?: unknown;
constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range);
}
export interface TypeHierarchyProvider {
/**
* Bootstraps type hierarchy by returning the item that is denoted by the given document
* and position. This item will be used as entry into the type graph. Providers should
* return `undefined` or `null` when there is no item at the given location.
*
* @param document The document in which the command was invoked.
* @param position The position at which the command was invoked.
* @param token A cancellation token.
* @returns A type hierarchy item or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
prepareTypeHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<TypeHierarchyItem[]>;
/**
* Provide all supertypes for an item, e.g all types from which a type is derived/inherited. In graph terms this describes directed
* and annotated edges inside the type graph, e.g the given item is the starting node and the result is the nodes
* that can be reached.
*
* @param item The hierarchy item for which super types should be computed.
* @param token A cancellation token.
* @returns A set of supertypes or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideTypeHierarchySupertypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult<TypeHierarchyItem[]>;
/**
* Provide all subtypes for an item, e.g all types which are derived/inherited from the given item. In
* graph terms this describes directed and annotated edges inside the type graph, e.g the given item is the starting
* node and the result is the nodes that can be reached.
*
* @param item The hierarchy item for which subtypes should be computed.
* @param token A cancellation token.
* @returns A set of subtypes or a thenable that resolves to such. The lack of a result can be
* signaled by returning `undefined` or `null`.
*/
provideTypeHierarchySubtypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult<TypeHierarchyItem[]>;
}
export namespace languages {
/**
* Register a type hierarchy provider.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A type hierarchy provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerTypeHierarchyProvider(selector: DocumentSelector, provider: TypeHierarchyProvider): Disposable;
}
//#endregion
}
......@@ -11,7 +11,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto, IdentifiableInlineCompletions, IdentifiableInlineCompletion } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ILanguageConfigurationDto, IRegExpDto, IIndentationRuleDto, IOnEnterRuleDto, ILocationDto, IWorkspaceSymbolDto, reviveWorkspaceEditDto, IDocumentFilterDto, IDefinitionLinkDto, ISignatureHelpProviderMetadataDto, ILinkDto, ICallHierarchyItemDto, ISuggestDataDto, ICodeActionDto, ISuggestDataDtoField, ISuggestResultDtoField, ICodeActionProviderMetadataDto, ILanguageWordDefinitionDto, IdentifiableInlineCompletions, IdentifiableInlineCompletion, ITypeHierarchyItemDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
......@@ -20,6 +20,7 @@ import { URI } from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import * as typeh from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { mixin } from 'vs/base/common/objects';
import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
......@@ -147,6 +148,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return data as callh.CallHierarchyItem;
}
private static _reviveTypeHierarchyItemDto(data: ITypeHierarchyItemDto | undefined): typeh.TypeHierarchyItem {
if (data) {
data.uri = URI.revive(data.uri);
}
return data as typeh.TypeHierarchyItem;
}
//#endregion
// --- outline
......@@ -773,6 +781,43 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
// --- type hierarchy
$registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void {
this._registrations.set(handle, typeh.TypeHierarchyProviderRegistry.register(selector, {
prepareTypeHierarchy: async (document, position, token) => {
const items = await this._proxy.$prepareTypeHierarchy(handle, document.uri, position, token);
if (!items) {
return undefined;
}
return {
dispose: () => {
for (const item of items) {
this._proxy.$releaseTypeHierarchy(handle, item._sessionId);
}
},
roots: items.map(MainThreadLanguageFeatures._reviveTypeHierarchyItemDto)
};
},
provideSupertypes: async (item, token) => {
const supertypes = await this._proxy.$provideTypeHierarchySupertypes(handle, item._sessionId, item._itemId, token);
if (!supertypes) {
return supertypes;
}
return supertypes.map(MainThreadLanguageFeatures._reviveTypeHierarchyItemDto);
},
provideSubtypes: async (item, token) => {
const subtypes = await this._proxy.$provideTypeHierarchySubtypes(handle, item._sessionId, item._itemId, token);
if (!subtypes) {
return subtypes;
}
return subtypes.map(MainThreadLanguageFeatures._reviveTypeHierarchyItemDto);
}
}));
}
}
export class MainThreadDocumentSemanticTokensProvider implements modes.DocumentSemanticTokensProvider {
......
......@@ -499,6 +499,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerInlayHintsProvider(selector: vscode.DocumentSelector, provider: vscode.InlayHintsProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerInlayHintsProvider(extension, selector, provider);
},
registerTypeHierarchyProvider(selector: vscode.DocumentSelector, provider: vscode.TypeHierarchyProvider): vscode.Disposable {
return extHostLanguageFeatures.registerTypeHierarchyProvider(extension, selector, provider);
}
};
......@@ -1232,6 +1235,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
ThemeIcon: extHostTypes.ThemeIcon,
TreeItem: extHostTypes.TreeItem,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
TypeHierarchyItem: extHostTypes.TypeHierarchyItem,
UIKind: UIKind,
Uri: URI,
ViewColumn: extHostTypes.ViewColumn,
......
......@@ -59,6 +59,7 @@ import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/que
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { ExtensionRunTestsRequest, ISerializedTestResults, ITestItem, ITestMessage, ITestRunTask, RunTestForControllerRequest, RunTestsRequest, ITestIdWithSrc, TestsDiff, IFileCoverage, CoverageDetails } from 'vs/workbench/contrib/testing/common/testCollection';
import { InternalTimelineOptions, Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
import { ActivationKind, ExtensionHostKind, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
......@@ -413,6 +414,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$emitFoldingRangeEvent(eventHandle: number, event?: any): void;
$registerSelectionRangeProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerCallHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$registerTypeHierarchyProvider(handle: number, selector: IDocumentFilterDto[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ILanguageConfigurationDto): void;
}
......@@ -1627,6 +1629,8 @@ export interface IInlineValueContextDto {
stoppedLocation: IRange;
}
export type ITypeHierarchyItemDto = Dto<TypeHierarchyItem>;
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<ICodeLensListDto | undefined>;
......@@ -1677,6 +1681,10 @@ export interface ExtHostLanguageFeaturesShape {
$provideCallHierarchyOutgoingCalls(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<IOutgoingCallDto[] | undefined>;
$releaseCallHierarchy(handle: number, sessionId: string): void;
$setWordDefinitions(wordDefinitions: ILanguageWordDefinitionDto[]): void;
$prepareTypeHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<ITypeHierarchyItemDto[] | undefined>;
$provideTypeHierarchySupertypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<ITypeHierarchyItemDto[] | undefined>;
$provideTypeHierarchySubtypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<ITypeHierarchyItemDto[] | undefined>;
$releaseTypeHierarchy(handle: number, sessionId: string): void;
}
export interface ExtHostQuickOpenShape {
......
......@@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri';
import type * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto } from 'vs/workbench/api/common/extHost.protocol';
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto, ITypeHierarchyItemDto } from 'vs/workbench/api/common/extHost.protocol';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
......@@ -406,6 +406,22 @@ const newCommands: ApiCommand[] = [
],
ApiCommandResult.Void
),
// --- type hierarchy
new ApiCommand(
'vscode.prepareTypeHierarchy', '_executePrepareTypeHierarchy', 'Prepare type hierarchy at a position inside a document',
[ApiCommandArgument.Uri, ApiCommandArgument.Position],
new ApiCommandResult<ITypeHierarchyItemDto[], types.TypeHierarchyItem[]>('A TypeHierarchyItem or undefined', v => v.map(typeConverters.TypeHierarchyItem.to))
),
new ApiCommand(
'vscode.provideSupertypes', '_executeProvideSupertypes', 'Compute supertypes for an item',
[ApiCommandArgument.TypeHierarchyItem],
new ApiCommandResult<ITypeHierarchyItemDto[], types.TypeHierarchyItem[]>('A TypeHierarchyItem or undefined', v => v.map(typeConverters.TypeHierarchyItem.to))
),
new ApiCommand(
'vscode.provideSubtypes', '_executeProvideSubtypes', 'Compute subtypes for an item',
[ApiCommandArgument.TypeHierarchyItem],
new ApiCommandResult<ITypeHierarchyItemDto[], types.TypeHierarchyItem[]>('A TypeHierarchyItem or undefined', v => v.map(typeConverters.TypeHierarchyItem.to))
),
];
//#endregion
......
......@@ -370,6 +370,7 @@ export class ApiCommandArgument<V, O = V> {
static readonly String = new ApiCommandArgument<string>('string', '', v => typeof v === 'string', v => v);
static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof extHostTypes.CallHierarchyItem, extHostTypeConverter.CallHierarchyItem.to);
static readonly TypeHierarchyItem = new ApiCommandArgument('item', 'A type hierarchy item', v => v instanceof extHostTypes.TypeHierarchyItem, extHostTypeConverter.TypeHierarchyItem.to);
constructor(
readonly name: string,
......
......@@ -1429,12 +1429,96 @@ class CallHierarchyAdapter {
}
}
class TypeHierarchyAdapter {
private readonly _idPool = new IdGenerator('');
private readonly _cache = new Map<string, Map<string, vscode.TypeHierarchyItem>>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.TypeHierarchyProvider
) { }
async prepareSession(uri: URI, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
const doc = this._documents.getDocument(uri);
const pos = typeConvert.Position.to(position);
const items = await this._provider.prepareTypeHierarchy(doc, pos, token);
if (!items) {
return undefined;
}
const sessionId = this._idPool.nextId();
this._cache.set(sessionId, new Map());
if (Array.isArray(items)) {
return items.map(item => this._cacheAndConvertItem(sessionId, item));
} else {
return [this._cacheAndConvertItem(sessionId, items)];
}
}
async provideSupertypes(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
const item = this._itemFromCache(sessionId, itemId);
if (!item) {
throw new Error('missing type hierarchy item');
}
const supertypes = await this._provider.provideTypeHierarchySupertypes(item, token);
if (!supertypes) {
return undefined;
}
return supertypes.map(supertype => {
return this._cacheAndConvertItem(sessionId, supertype);
});
}
async provideSubtypes(sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
const item = this._itemFromCache(sessionId, itemId);
if (!item) {
throw new Error('missing type hierarchy item');
}
const subtypes = await this._provider.provideTypeHierarchySubtypes(item, token);
if (!subtypes) {
return undefined;
}
return subtypes.map(subtype => {
return this._cacheAndConvertItem(sessionId, subtype);
});
}
releaseSession(sessionId: string): void {
this._cache.delete(sessionId);
}
private _cacheAndConvertItem(sessionId: string, item: vscode.TypeHierarchyItem): extHostProtocol.ITypeHierarchyItemDto {
const map = this._cache.get(sessionId)!;
const dto: extHostProtocol.ICallHierarchyItemDto = {
_sessionId: sessionId,
_itemId: map.size.toString(36),
name: item.name,
detail: item.detail,
kind: typeConvert.SymbolKind.from(item.kind),
uri: item.uri,
range: typeConvert.Range.from(item.range),
selectionRange: typeConvert.Range.from(item.selectionRange),
tags: item.tags?.map(typeConvert.SymbolTag.from)
};
map.set(dto._itemId, item);
return dto;
}
private _itemFromCache(sessionId: string, itemId: string): vscode.TypeHierarchyItem | undefined {
const map = this._cache.get(sessionId);
return map?.get(itemId);
}
}
type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
| SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter
| DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter
| EvaluatableExpressionAdapter | InlineValuesAdapter
| LinkedEditingRangeAdapter | InlayHintsAdapter | InlineCompletionAdapter;
......@@ -2043,6 +2127,29 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
this._withAdapter(handle, CallHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined);
}
// --- type hierarchy
registerTypeHierarchyProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeHierarchyProvider): vscode.Disposable {
const handle = this._addNewAdapter(new TypeHierarchyAdapter(this._documents, provider), extension);
this._proxy.$registerTypeHierarchyProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$prepareTypeHierarchy(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => Promise.resolve(adapter.prepareSession(URI.revive(resource), position, token)), undefined);
}
$provideTypeHierarchySupertypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => adapter.provideSupertypes(sessionId, itemId, token), undefined);
}
$provideTypeHierarchySubtypes(handle: number, sessionId: string, itemId: string, token: CancellationToken): Promise<extHostProtocol.ITypeHierarchyItemDto[] | undefined> {
return this._withAdapter(handle, TypeHierarchyAdapter, adapter => adapter.provideSubtypes(sessionId, itemId, token), undefined);
}
$releaseTypeHierarchy(handle: number, sessionId: string): void {
this._withAdapter(handle, TypeHierarchyAdapter, adapter => Promise.resolve(adapter.releaseSession(sessionId)), undefined);
}
// --- configuration
private static _serializeRegExp(regExp: RegExp): extHostProtocol.IRegExpDto {
......
......@@ -1780,3 +1780,22 @@ export namespace CodeActionTriggerKind {
}
}
}
export namespace TypeHierarchyItem {
export function to(item: extHostProtocol.ITypeHierarchyItemDto): types.TypeHierarchyItem {
const result = new types.TypeHierarchyItem(
SymbolKind.to(item.kind),
item.name,
item.detail || '',
URI.revive(item.uri),
Range.to(item.range),
Range.to(item.selectionRange)
);
result._sessionId = item._sessionId;
result._itemId = item._itemId;
return result;
}
}
......@@ -3539,3 +3539,24 @@ export enum PortAutoForwardAction {
Ignore = 5,
OpenBrowserOnce = 6
}
export class TypeHierarchyItem {
_sessionId?: string;
_itemId?: string;
kind: SymbolKind;
name: string;
detail?: string;
uri: URI;
range: Range;
selectionRange: Range;
constructor(kind: SymbolKind, name: string, detail: string, uri: URI, range: Range, selectionRange: Range) {
this.kind = kind;
this.name = name;
this.detail = detail;
this.uri = uri;
this.range = range;
this.selectionRange = selectionRange;
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Event } from 'vs/base/common/event';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { localize } from 'vs/nls';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { TypeHierarchyProviderRegistry } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy';
const _ctxHasTypeHierarchyProvider = new RawContextKey<boolean>('editorHasTypeHierarchyProvider', false, localize('editorHasTypeHierarchyProvider', 'Whether a type hierarchy provider is available'));
class TypeHierarchyController implements IEditorContribution {
static readonly Id = 'typeHierarchy';
static get(editor: ICodeEditor): TypeHierarchyController {
return editor.getContribution<TypeHierarchyController>(TypeHierarchyController.Id);
}
private readonly _ctxHasProvider: IContextKey<boolean>;
private readonly _dispoables = new DisposableStore();
private readonly _sessionDisposables = new DisposableStore();
constructor(
readonly _editor: ICodeEditor,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
) {
this._ctxHasProvider = _ctxHasTypeHierarchyProvider.bindTo(this._contextKeyService);
this._dispoables.add(Event.any<any>(_editor.onDidChangeModel, _editor.onDidChangeModelLanguage, TypeHierarchyProviderRegistry.onDidChange)(() => {
this._ctxHasProvider.set(_editor.hasModel() && TypeHierarchyProviderRegistry.has(_editor.getModel()));
}));
this._dispoables.add(this._sessionDisposables);
}
dispose(): void {
this._dispoables.dispose();
}
}
registerEditorContribution(TypeHierarchyController.Id, TypeHierarchyController);
// Testing
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRange } from 'vs/editor/common/core/range';
import { SymbolKind, ProviderResult, SymbolTag } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { CancellationToken } from 'vs/base/common/cancellation';
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
import { URI } from 'vs/base/common/uri';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
export interface TypeHierarchyItem {
_sessionId: string;
_itemId: string;
name: string;
kind: SymbolKind;
detail?: string;
uri: URI;
range: IRange;
selectionRange: IRange;
tags?: SymbolTag[]
}
export interface TypeHierarchySession {
roots: TypeHierarchyItem[];
dispose(): void;
}
export interface TypeHierarchyProvider {
prepareTypeHierarchy(document: ITextModel, position: IPosition, token: CancellationToken): ProviderResult<TypeHierarchySession>;
provideSupertypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult<TypeHierarchyItem[]>;
provideSubtypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult<TypeHierarchyItem[]>;
}
export const TypeHierarchyProviderRegistry = new LanguageFeatureRegistry<TypeHierarchyProvider>();
class RefCountedDisposabled {
constructor(
private readonly _disposable: IDisposable,
private _counter = 1
) { }
acquire() {
this._counter++;
return this;
}
release() {
if (--this._counter === 0) {
this._disposable.dispose();
}
return this;
}
}
export class TypeHierarchyModel {
static async create(model: ITextModel, position: IPosition, token: CancellationToken): Promise<TypeHierarchyModel | undefined> {
const [provider] = TypeHierarchyProviderRegistry.ordered(model);
if (!provider) {
return undefined;
}
const session = await provider.prepareTypeHierarchy(model, position, token);
if (!session) {
return undefined;
}
return new TypeHierarchyModel(session.roots.reduce((p, c) => p + c._sessionId, ''), provider, session.roots, new RefCountedDisposabled(session));
}
readonly root: TypeHierarchyItem;
private constructor(
readonly id: string,
readonly provider: TypeHierarchyProvider,
readonly roots: TypeHierarchyItem[],
readonly ref: RefCountedDisposabled,
) {
this.root = roots[0];
}
dispose(): void {
this.ref.release();
}
fork(item: TypeHierarchyItem): TypeHierarchyModel {
const that = this;
return new class extends TypeHierarchyModel {
constructor() {
super(that.id, that.provider, [item], that.ref.acquire());
}
};
}
async provideSupertypes(item: TypeHierarchyItem, token: CancellationToken): Promise<TypeHierarchyItem[]> {
try {
const result = await this.provider.provideSupertypes(item, token);
if (isNonEmptyArray(result)) {
return result;
}
} catch (e) {
onUnexpectedExternalError(e);
}
return [];
}
async provideSubtypes(item: TypeHierarchyItem, token: CancellationToken): Promise<TypeHierarchyItem[]> {
try {
const result = await this.provider.provideSubtypes(item, token);
if (isNonEmptyArray(result)) {
return result;
}
} catch (e) {
onUnexpectedExternalError(e);
}
return [];
}
}
// --- API command support
const _models = new Map<string, TypeHierarchyModel>();
CommandsRegistry.registerCommand('_executePrepareTypeHierarchy', async (accessor, ...args) => {
const [resource, position] = args;
assertType(URI.isUri(resource));
assertType(Position.isIPosition(position));
const modelService = accessor.get(IModelService);
let textModel = modelService.getModel(resource);
let textModelReference: IDisposable | undefined;
if (!textModel) {
const textModelService = accessor.get(ITextModelService);
const result = await textModelService.createModelReference(resource);
textModel = result.object.textEditorModel;
textModelReference = result;
}
try {
const model = await TypeHierarchyModel.create(textModel, position, CancellationToken.None);
if (!model) {
return [];
}
_models.set(model.id, model);
_models.forEach((value, key, map) => {
if (map.size > 10) {
value.dispose();
_models.delete(key);
}
});
return [model.root];
} finally {
textModelReference?.dispose();
}
});
function isTypeHierarchyItemDto(obj: any): obj is TypeHierarchyItem {
return true;
}
CommandsRegistry.registerCommand('_executeProvideSupertypes', async (_accessor, ...args) => {
const [item] = args;
assertType(isTypeHierarchyItemDto(item));
// find model
const model = _models.get(item._sessionId);
if (!model) {
return undefined;
}
return model.provideSupertypes(item, CancellationToken.None);
});
CommandsRegistry.registerCommand('_executeProvideSubtypes', async (_accessor, ...args) => {
const [item] = args;
assertType(isTypeHierarchyItemDto(item));
// find model
const model = _models.get(item._sessionId);
if (!model) {
return undefined;
}
return model.provideSubtypes(item, CancellationToken.None);
});
......@@ -292,6 +292,9 @@ import 'vs/workbench/contrib/welcome/common/newFile.contribution';
// Call Hierarchy
import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution';
// Type Hierarchy
import 'vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution';
// Outline
import 'vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline';
import 'vs/workbench/contrib/outline/browser/outline.contribution';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册