From f01a647224be1d9772df20859a62043b13c5d40a Mon Sep 17 00:00:00 2001 From: Colin Grant Date: Mon, 28 Jun 2021 14:36:05 -0500 Subject: [PATCH] Implement missing functionality for call hierarchy API Signed-off-by: Colin Grant --- CHANGELOG.md | 1 + .../src/browser/callhierarchy-contribution.ts | 9 +-- .../browser/callhierarchy-frontend-module.ts | 1 - .../src/browser/current-editor-access.ts | 3 + .../plugin-ext-vscode/compile.tsconfig.json | 3 + packages/plugin-ext-vscode/package.json | 1 + .../plugin-vscode-commands-contribution.ts | 69 ++++++++++++++++--- .../plugin-ext/src/common/plugin-api-rpc.ts | 1 + .../callhierarchy-type-converters.ts | 47 ++++++++++--- .../src/main/browser/command-registry-main.ts | 2 +- .../src/main/browser/languages-main.ts | 24 ++++--- .../main/browser/view/tree-view-widget.tsx | 5 +- packages/plugin-ext/src/plugin/documents.ts | 8 +-- .../plugin-ext/src/plugin/known-commands.ts | 21 +++--- packages/plugin-ext/src/plugin/languages.ts | 18 ++--- .../src/plugin/languages/call-hierarchy.ts | 38 +++++++--- .../plugin-ext/src/plugin/type-converters.ts | 44 +++++++----- packages/plugin-ext/src/plugin/types-impl.ts | 16 +++++ .../timeline/src/browser/timeline-widget.tsx | 14 ++-- 19 files changed, 233 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 852d14f0f5c0c..45fefab2f74d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ [Breaking Changes:](#breaking_changes_1.16.0) - [monaco] upgraded to monaco 0.23.0 including replacement of `quickOpen` API (0.20.x) with `quickInput` API (0.23.x) [#9154](https://github.com/eclipse-theia/theia/pull/9154) +- [call-hierarchy] `CurrentEditorAccess` is deprecated. Use the version implemented in the `editor` package instead. The services in `call-hierarchy` that previously used the local `CurrentEditorAccess` no longer do. [#9681](https://github.com/eclipse-theia/theia/pull/9681) - [workspace] `WorkspaceCommandContribution.addFolderToWorkspace` no longer accepts `undefined`. `WorkspaceService.addRoot` now accepts a URI or a URI[]. [#9684](https://github.com/eclipse-theia/theia/pull/9684) ## v1.15.0 - 6/30/2021 diff --git a/packages/callhierarchy/src/browser/callhierarchy-contribution.ts b/packages/callhierarchy/src/browser/callhierarchy-contribution.ts index 4e7cdcdecef30..c128a049ffc95 100644 --- a/packages/callhierarchy/src/browser/callhierarchy-contribution.ts +++ b/packages/callhierarchy/src/browser/callhierarchy-contribution.ts @@ -17,10 +17,9 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core/lib/common'; import { AbstractViewContribution, OpenViewArguments, KeybindingRegistry } from '@theia/core/lib/browser'; -import { EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser'; +import { EDITOR_CONTEXT_MENU, CurrentEditorAccess } from '@theia/editor/lib/browser'; import { CallHierarchyTreeWidget } from './callhierarchy-tree/callhierarchy-tree-widget'; import { CALLHIERARCHY_ID } from './callhierarchy'; -import { CurrentEditorAccess } from './current-editor-access'; import { CallHierarchyServiceProvider } from './callhierarchy-service'; import URI from '@theia/core/lib/common/uri'; @@ -52,15 +51,13 @@ export class CallHierarchyContribution extends AbstractViewContribution): Promise { const widget = await super.openView(args); - const selection = this.editorAccess.getSelection(); - const languageId = this.editorAccess.getLanguageId(); + const { selection, languageId } = this.editorAccess; widget.initializeModel(selection, languageId); return widget; } diff --git a/packages/callhierarchy/src/browser/callhierarchy-frontend-module.ts b/packages/callhierarchy/src/browser/callhierarchy-frontend-module.ts index 4be9415268b59..c41509df440ab 100644 --- a/packages/callhierarchy/src/browser/callhierarchy-frontend-module.ts +++ b/packages/callhierarchy/src/browser/callhierarchy-frontend-module.ts @@ -21,7 +21,6 @@ import { WidgetFactory, bindViewContribution } from '@theia/core/lib/browser'; import { CALLHIERARCHY_ID } from './callhierarchy'; import { createHierarchyTreeWidget } from './callhierarchy-tree'; import { CurrentEditorAccess } from './current-editor-access'; - import { ContainerModule } from '@theia/core/shared/inversify'; import '../../src/browser/style/index.css'; diff --git a/packages/callhierarchy/src/browser/current-editor-access.ts b/packages/callhierarchy/src/browser/current-editor-access.ts index 613f78952624c..b66a8e4c8ea11 100644 --- a/packages/callhierarchy/src/browser/current-editor-access.ts +++ b/packages/callhierarchy/src/browser/current-editor-access.ts @@ -18,6 +18,9 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { EditorManager, TextEditor } from '@theia/editor/lib/browser'; import { Location } from '@theia/core/shared/vscode-languageserver-types'; +/** + * @deprecated since 1.15.0. Import from `@theia/editor` instead. + */ @injectable() export class CurrentEditorAccess { diff --git a/packages/plugin-ext-vscode/compile.tsconfig.json b/packages/plugin-ext-vscode/compile.tsconfig.json index f8a2ccde4236c..01282b0662859 100644 --- a/packages/plugin-ext-vscode/compile.tsconfig.json +++ b/packages/plugin-ext-vscode/compile.tsconfig.json @@ -43,6 +43,9 @@ }, { "path": "../terminal/compile.tsconfig.json" + }, + { + "path": "../callhierarchy/compile.tsconfig.json" } ] } diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 207e0843ebe4b..319c8c671a2da 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -3,6 +3,7 @@ "version": "1.15.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { + "@theia/callhierarchy": "1.15.0", "@theia/core": "1.15.0", "@theia/editor": "1.15.0", "@theia/filesystem": "1.15.0", diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index 61e40d2099b19..c4623ed49ed39 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Command, CommandContribution, CommandRegistry, environment, isOSX } from '@theia/core'; +import { Command, CommandContribution, CommandRegistry, environment, isOSX, CancellationTokenSource } from '@theia/core'; import { ApplicationShell, CommonCommands, @@ -45,7 +45,7 @@ import { } from '@theia/plugin-ext/lib/common/plugin-api-rpc-model'; import { DocumentsMainImpl } from '@theia/plugin-ext/lib/main/browser/documents-main'; import { createUntitledURI } from '@theia/plugin-ext/lib/main/browser/editor/untitled-resource'; -import { isUriComponents, toDocumentSymbol } from '@theia/plugin-ext/lib/plugin/type-converters'; +import { isUriComponents, toDocumentSymbol, toPosition } from '@theia/plugin-ext/lib/plugin/type-converters'; import { ViewColumn } from '@theia/plugin-ext/lib/plugin/types-impl'; import { WorkspaceCommands } from '@theia/workspace/lib/browser'; import { WorkspaceService, WorkspaceInput } from '@theia/workspace/lib/browser/workspace-service'; @@ -65,6 +65,14 @@ import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '@theia/navigator/lib/bro import { SelectableTreeNode } from '@theia/core/lib/browser/tree/tree-selection'; import { UriComponents } from '@theia/plugin-ext/lib/common/uri-components'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { CallHierarchyServiceProvider, CallHierarchyService } from '@theia/callhierarchy/lib/browser'; +import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; +import { + fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall, + fromCallHierarchyCallerToModelCallHierarchyIncomingCall, + fromDefinition, + toDefinition +} from '@theia/plugin-ext/lib/main/browser/callhierarchy/callhierarchy-type-converters'; export namespace VscodeCommands { export const OPEN: Command = { @@ -116,6 +124,10 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly pluginServer: PluginServer; @inject(FileService) protected readonly fileService: FileService; + @inject(CallHierarchyServiceProvider) + protected readonly callHierarchyProvider: CallHierarchyServiceProvider; + @inject(MonacoTextModelService) + protected readonly textModelService: MonacoTextModelService; registerCommands(commands: CommandRegistry): void { commands.registerCommand(VscodeCommands.OPEN, { @@ -526,8 +538,19 @@ export class PluginVscodeCommandsContribution implements CommandContribution { id: 'vscode.prepareCallHierarchy' }, { - execute: ((resource: URI, position: Position) => - commands.executeCommand('_executePrepareCallHierarchy', monaco.Uri.from(resource), position)) + execute: async (resource: URI, position: Position): Promise => { + const provider = await this.getCallHierarchyServiceForUri(resource); + const definition = await provider?.getRootDefinition( + resource.fsPath, + toPosition(position), + new CancellationTokenSource().token + ); + if (definition) { + const item = fromDefinition(definition); + return [item]; + }; + return []; + } } ); commands.registerCommand( @@ -535,17 +558,37 @@ export class PluginVscodeCommandsContribution implements CommandContribution { id: 'vscode.provideIncomingCalls' }, { - execute: ((item: CallHierarchyItem) => - commands.executeCommand('_executeProvideIncomingCalls', { item })) - } + execute: async (item: CallHierarchyItem): Promise => { + const resource = URI.from(item.uri); + const provider = await this.getCallHierarchyServiceForUri(resource); + const incomingCalls = await provider?.getCallers( + toDefinition(item), + new CancellationTokenSource().token, + ); + if (incomingCalls) { + return incomingCalls.map(fromCallHierarchyCallerToModelCallHierarchyIncomingCall); + } + return []; + }, + }, ); commands.registerCommand( { id: 'vscode.provideOutgoingCalls' }, { - execute: ((item: CallHierarchyItem) => - commands.executeCommand('_executeProvideOutgoingCalls', { item })) + execute: async (item: CallHierarchyItem): Promise => { + const resource = URI.from(item.uri); + const provider = await this.getCallHierarchyServiceForUri(resource); + const outgoingCalls = await provider?.getCallees?.( + toDefinition(item), + new CancellationTokenSource().token, + ); + if (outgoingCalls) { + return outgoingCalls.map(fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall); + } + return []; + } } ); @@ -626,4 +669,12 @@ export class PluginVscodeCommandsContribution implements CommandContribution { } }); } + + protected async getCallHierarchyServiceForUri(resource: URI): Promise { + const reference = await this.textModelService.createModelReference(resource); + const uri = new TheiaURI(resource); + const languageId = reference.object.languageId; + reference.dispose(); + return this.callHierarchyProvider.get(languageId, uri); + } } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index cbce7991a9ffd..1a1da7fbe4db0 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1459,6 +1459,7 @@ export interface LanguagesExt { $provideDocumentRangeSemanticTokens(handle: number, resource: UriComponents, range: Range, token: CancellationToken): Promise; $provideRootDefinition(handle: number, resource: UriComponents, location: Position, token: CancellationToken): Promise; $provideCallers(handle: number, definition: CallHierarchyDefinition, token: CancellationToken): Promise; + $provideCallees(handle: number, definition: CallHierarchyDefinition, token: CancellationToken): Promise; } export const LanguagesMainFactory = Symbol('LanguagesMainFactory'); diff --git a/packages/plugin-ext/src/main/browser/callhierarchy/callhierarchy-type-converters.ts b/packages/plugin-ext/src/main/browser/callhierarchy/callhierarchy-type-converters.ts index 38efe4ac45e83..20be1da3611c3 100644 --- a/packages/plugin-ext/src/main/browser/callhierarchy/callhierarchy-type-converters.ts +++ b/packages/plugin-ext/src/main/browser/callhierarchy/callhierarchy-type-converters.ts @@ -14,13 +14,12 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { Definition as CallHierarchyDefinition, Caller as CallHierarchyCaller } from '@theia/callhierarchy/lib/browser'; +import { Definition as CallHierarchyDefinition, Caller as CallHierarchyCaller, Callee as CallHierarchyCallee } from '@theia/callhierarchy/lib/browser'; import * as model from '../../../common/plugin-api-rpc-model'; import * as rpc from '../../../common/plugin-api-rpc'; import * as callhierarchy from '@theia/core/shared/vscode-languageserver-types'; import { URI } from '@theia/core/shared/vscode-uri'; import { UriComponents } from '../../../common/uri-components'; -import { Location } from '@theia/core/shared/vscode-languageserver-types'; export function toUriComponents(uri: string): UriComponents { return URI.parse(uri); @@ -30,15 +29,15 @@ export function fromUriComponents(uri: UriComponents): string { return URI.revive(uri).toString(); } -export function fromLocation(location: Location): model.Location { +export function fromLocation(location: callhierarchy.Location): model.Location { return { uri: URI.parse(location.uri), range: fromRange(location.range) }; } -export function toLocation(uri: UriComponents, range: model.Range): Location { - return { +export function toLocation(uri: UriComponents, range: model.Range): callhierarchy.Location { + return { uri: URI.revive(uri).toString(), range: toRange(range) }; @@ -54,15 +53,20 @@ export function fromPosition(position: callhierarchy.Position): rpc.Position { export function fromRange(range: callhierarchy.Range): model.Range { const { start, end } = range; return { - startLineNumber: start.line, - startColumn: start.character, - endLineNumber: end.line, - endColumn: end.character + startLineNumber: start.line + 1, + startColumn: start.character + 1, + endLineNumber: end.line + 1, + endColumn: end.character + 1, }; } export function toRange(range: model.Range): callhierarchy.Range { - return callhierarchy.Range.create(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + return callhierarchy.Range.create( + range.startLineNumber - 1, + range.startColumn - 1, + range.endLineNumber - 1, + range.endColumn - 1, + ); } export namespace SymbolKindConverter { @@ -169,8 +173,29 @@ export function toCaller(caller: model.CallHierarchyReference): CallHierarchyCal } export function fromCaller(caller: CallHierarchyCaller): model.CallHierarchyReference { - return { + return { callerDefinition: fromDefinition(caller.callerDefinition), references: caller.references.map(fromRange) }; } + +export function toCallee(callee: model.CallHierarchyReference): CallHierarchyCallee { + return { + calleeDefinition: toDefinition(callee.callerDefinition), + references: callee.references.map(toRange), + }; +} + +export function fromCallHierarchyCallerToModelCallHierarchyIncomingCall(caller: CallHierarchyCaller): model.CallHierarchyIncomingCall { + return { + from: fromDefinition(caller.callerDefinition), + fromRanges: caller.references.map(fromRange), + }; +} + +export function fromCallHierarchyCalleeToModelCallHierarchyOutgoingCall(callee: CallHierarchyCallee): model.CallHierarchyOutgoingCall { + return { + to: fromDefinition(callee.calleeDefinition), + fromRanges: callee.references.map(fromRange), + }; +} diff --git a/packages/plugin-ext/src/main/browser/command-registry-main.ts b/packages/plugin-ext/src/main/browser/command-registry-main.ts index 6d0799b5495a7..affd3b00d57a3 100644 --- a/packages/plugin-ext/src/main/browser/command-registry-main.ts +++ b/packages/plugin-ext/src/main/browser/command-registry-main.ts @@ -77,7 +77,7 @@ export class CommandRegistryMainImpl implements CommandRegistryMain, Disposable throw new Error(`Command with id '${id}' is not registered.`); } try { - return await this.delegate.executeCommand(id, ...args); + return await this.delegate.executeCommand(id, ...args); } catch (e) { // Command handler may be not active at the moment so the error must be caught. See https://github.com/eclipse-theia/theia/pull/6687#discussion_r354810079 if ('code' in e && e['code'] === 'NO_ACTIVE_HANDLER') { diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts index 193586778b587..01fd332be432c 100644 --- a/packages/plugin-ext/src/main/browser/languages-main.ts +++ b/packages/plugin-ext/src/main/browser/languages-main.ts @@ -50,8 +50,8 @@ import * as theia from '@theia/plugin'; import { UriComponents } from '../../common/uri-components'; import { CancellationToken } from '@theia/core/lib/common'; import { LanguageSelector, RelativePattern } from '@theia/callhierarchy/lib/common/language-selector'; -import { CallHierarchyService, CallHierarchyServiceProvider, Caller, Definition } from '@theia/callhierarchy/lib/browser'; -import { toDefinition, toUriComponents, fromDefinition, fromPosition, toCaller } from './callhierarchy/callhierarchy-type-converters'; +import { CallHierarchyService, CallHierarchyServiceProvider, Definition } from '@theia/callhierarchy/lib/browser'; +import { toDefinition, toUriComponents, fromDefinition, fromPosition, toCaller, toCallee } from './callhierarchy/callhierarchy-type-converters'; import { Position, DocumentUri } from '@theia/core/shared/vscode-languageserver-types'; import { ObjectIdentifier } from '../../common/object-identifier'; import { mixin } from '../../common/types'; @@ -782,15 +782,23 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { } if (Array.isArray(result)) { - const callers: Caller[] = []; - for (const item of result) { - callers.push(toCaller(item)); - } - return callers; + return result.map(toCaller); } return undefined!; - }) + }), + + getCallees: (definition: Definition, cancellationToken: CancellationToken) => this.proxy.$provideCallees(handle, fromDefinition(definition), cancellationToken) + .then(result => { + if (!result) { + return undefined; + } + if (Array.isArray(result)) { + return result.map(toCallee); + } + + return undefined; + }), }; } diff --git a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx index abe6e5b4fe601..4a89cb8586a40 100644 --- a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx +++ b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx @@ -154,7 +154,8 @@ export class PluginTree extends TreeImpl { themeIconId, resourceUri, tooltip: item.tooltip, - contextValue: item.contextValue + contextValue: item.contextValue, + command: item.command, }; const node = this.getNode(item.id); if (item.collapsibleState !== undefined && item.collapsibleState !== TreeViewItemCollapsibleState.None) { @@ -179,7 +180,7 @@ export class PluginTree extends TreeImpl { parent, visible: true, selected: false, - command: item.command + command: item.command, }, update); } diff --git a/packages/plugin-ext/src/plugin/documents.ts b/packages/plugin-ext/src/plugin/documents.ts index 374916c135991..32894dfceb30e 100644 --- a/packages/plugin-ext/src/plugin/documents.ts +++ b/packages/plugin-ext/src/plugin/documents.ts @@ -255,10 +255,10 @@ export class DocumentsExtImpl implements DocumentsExt { if (options.selection) { const { start, end } = options.selection; selection = { - startLineNumber: start.line, - startColumn: start.character, - endLineNumber: end.line, - endColumn: end.character + startLineNumber: start.line + 1, + startColumn: start.character + 1, + endLineNumber: end.line + 1, + endColumn: end.character + 1 }; } documentOptions = { diff --git a/packages/plugin-ext/src/plugin/known-commands.ts b/packages/plugin-ext/src/plugin/known-commands.ts index b0f9bda826468..eb6e3fdddb6d0 100755 --- a/packages/plugin-ext/src/plugin/known-commands.ts +++ b/packages/plugin-ext/src/plugin/known-commands.ts @@ -17,13 +17,13 @@ import { Range as R, Position as P, Location as L } from '@theia/core/shared/vscode-languageserver-types'; import * as theia from '@theia/plugin'; import { cloneAndChange } from '../common/objects'; -import { Position, Range, Location, CallHierarchyItem, URI } from './types-impl'; +import { Position, Range, Location, CallHierarchyItem, URI, TextDocumentShowOptions } from './types-impl'; import { fromPosition, fromRange, fromLocation, isModelLocation, toLocation, isModelCallHierarchyItem, fromCallHierarchyItem, toCallHierarchyItem, isModelCallHierarchyIncomingCall, toCallHierarchyIncomingCall, - isModelCallHierarchyOutgoingCall, toCallHierarchyOutgoingCall + isModelCallHierarchyOutgoingCall, toCallHierarchyOutgoingCall, fromTextDocumentShowOptions } from './type-converters'; // Here is a mapping of VSCode commands to monaco commands with their conversions @@ -294,6 +294,8 @@ export namespace KnownCommands { mappings['vscode.prepareCallHierarchy'] = ['vscode.prepareCallHierarchy', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.provideIncomingCalls'] = ['vscode.provideIncomingCalls', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.provideOutgoingCalls'] = ['vscode.provideOutgoingCalls', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; + mappings['vscode.open'] = ['vscode.open', CONVERT_VSCODE_TO_MONACO]; + mappings['vscode.diff'] = ['vscode.diff', CONVERT_VSCODE_TO_MONACO]; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function map(id: string, args: any[] | undefined, toDo: (mappedId: string, mappedArgs: any[] | undefined, mappedResult: ConversionFunction | undefined) => T): T { @@ -346,6 +348,12 @@ export namespace KnownCommands { function vscodeToMonacoArgsConverter(args: any[]) { // tslint:disable-next-line:typedef return cloneAndChange(args, function (value) { + if (CallHierarchyItem.isCallHierarchyItem(value)) { + return fromCallHierarchyItem(value); + } + if (TextDocumentShowOptions.isTextDocumentShowOptions(value)) { + return fromTextDocumentShowOptions(value); + } if (Position.isPosition(value)) { return fromPosition(value); } @@ -355,9 +363,6 @@ export namespace KnownCommands { if (Location.isLocation(value)) { return fromLocation(value); } - if (CallHierarchyItem.isCallHierarchyItem(value)) { - return fromCallHierarchyItem(value); - } if (!Array.isArray(value)) { return value; } @@ -368,9 +373,6 @@ export namespace KnownCommands { function monacoToVscodeArgsConverter(args: any[]) { // tslint:disable-next-line:typedef return cloneAndChange(args, function (value) { - if (isModelLocation(value)) { - return toLocation(value); - } if (isModelCallHierarchyItem(value)) { return toCallHierarchyItem(value); } @@ -380,6 +382,9 @@ export namespace KnownCommands { if (isModelCallHierarchyOutgoingCall(value)) { return toCallHierarchyOutgoingCall(value); } + if (isModelLocation(value)) { + return toLocation(value); + } if (!Array.isArray(value)) { return value; } diff --git a/packages/plugin-ext/src/plugin/languages.ts b/packages/plugin-ext/src/plugin/languages.ts index 43b0365204541..c8e538f9bdb71 100644 --- a/packages/plugin-ext/src/plugin/languages.ts +++ b/packages/plugin-ext/src/plugin/languages.ts @@ -91,7 +91,6 @@ import { CallHierarchyAdapter } from './languages/call-hierarchy'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { DocumentSemanticTokensAdapter, DocumentRangeSemanticTokensAdapter } from './languages/semantic-highlighting'; -/* eslint-disable @typescript-eslint/indent */ type Adapter = CompletionAdapter | SignatureHelpAdapter | HoverAdapter | @@ -116,7 +115,6 @@ type Adapter = CompletionAdapter | CallHierarchyAdapter | DocumentRangeSemanticTokensAdapter | DocumentSemanticTokensAdapter; -/* eslint-enable @typescript-eslint/indent */ export class LanguagesExtImpl implements LanguagesExt { @@ -200,7 +198,7 @@ export class LanguagesExtImpl implements LanguagesExt { return fallbackValue; } if (adapter instanceof ctor) { - return callback(adapter); + return callback(adapter); } throw new Error('no adapter found'); } @@ -591,6 +589,10 @@ export class LanguagesExtImpl implements LanguagesExt { $provideCallers(handle: number, definition: CallHierarchyDefinition, token: theia.CancellationToken): Promise { return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallers(definition, token), undefined); } + + $provideCallees(handle: number, definition: CallHierarchyDefinition, token: theia.CancellationToken): Promise { + return this.withAdapter(handle, CallHierarchyAdapter, adapter => adapter.provideCallees(definition, token), undefined); + } // ### Call Hierarchy Provider end // #region semantic coloring @@ -640,11 +642,11 @@ function serializeEnterRules(rules?: theia.OnEnterRule[]): SerializedOnEnterRule } return rules.map(r => - ({ - action: r.action, - beforeText: serializeRegExp(r.beforeText), - afterText: serializeRegExp(r.afterText) - } as SerializedOnEnterRule)); + ({ + action: r.action, + beforeText: serializeRegExp(r.beforeText), + afterText: serializeRegExp(r.afterText) + } as SerializedOnEnterRule)); } function serializeRegExp(regexp?: RegExp): SerializedRegExp | undefined { diff --git a/packages/plugin-ext/src/plugin/languages/call-hierarchy.ts b/packages/plugin-ext/src/plugin/languages/call-hierarchy.ts index aeab6ba8936e9..8a94441e25ac6 100644 --- a/packages/plugin-ext/src/plugin/languages/call-hierarchy.ts +++ b/packages/plugin-ext/src/plugin/languages/call-hierarchy.ts @@ -45,7 +45,7 @@ export class CallHierarchyAdapter { return undefined; } - return this.fromCallHierarchyitem(definition); + return this.fromCallHierarchyItem(definition); } async provideCallers(definition: model.CallHierarchyDefinition, token: theia.CancellationToken): Promise { @@ -57,7 +57,16 @@ export class CallHierarchyAdapter { return callers.map(item => this.fromCallHierarchyIncomingCall(item)); } - private fromCallHierarchyitem(item: theia.CallHierarchyItem): model.CallHierarchyDefinition { + async provideCallees(definition: model.CallHierarchyDefinition, token: theia.CancellationToken): Promise { + const callees = await this.provider.provideCallHierarchyOutgoingCalls(this.toCallHierarchyItem(definition), token); + if (!callees) { + return undefined; + } + + return callees.map(item => this.fromCallHierarchyOutgoingCall(item)); + } + + private fromCallHierarchyItem(item: theia.CallHierarchyItem): model.CallHierarchyDefinition { return { uri: item.uri, range: this.fromRange(item.range), @@ -69,19 +78,19 @@ export class CallHierarchyAdapter { private fromRange(range: theia.Range): model.Range { return { - startLineNumber: range.start.line, - startColumn: range.start.character, - endLineNumber: range.end.line, - endColumn: range.end.character + startLineNumber: range.start.line + 1, + startColumn: range.start.character + 1, + endLineNumber: range.end.line + 1, + endColumn: range.end.character + 1, }; } private toRange(range: model.Range): types.Range { return new types.Range( - range.startLineNumber, - range.startColumn, - range.endLineNumber, - range.endColumn + range.startLineNumber - 1, + range.startColumn - 1, + range.endLineNumber - 1, + range.endColumn - 1, ); } @@ -98,8 +107,15 @@ export class CallHierarchyAdapter { private fromCallHierarchyIncomingCall(caller: theia.CallHierarchyIncomingCall): model.CallHierarchyReference { return { - callerDefinition: this.fromCallHierarchyitem(caller.from), + callerDefinition: this.fromCallHierarchyItem(caller.from), references: caller.fromRanges.map(l => this.fromRange(l)) }; } + + protected fromCallHierarchyOutgoingCall(caller: theia.CallHierarchyOutgoingCall): model.CallHierarchyReference { + return { + callerDefinition: this.fromCallHierarchyItem(caller.to), + references: caller.fromRanges.map(this.fromRange.bind(this)), + }; + } } diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 851d49ea10633..9580d1515d79a 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import * as theia from '@theia/plugin'; -import { Position as P, Range as R, SymbolInformation, SymbolKind as S } from '@theia/core/shared/vscode-languageserver-types'; +import * as lstypes from '@theia/core/shared/vscode-languageserver-types'; import { URI } from './types-impl'; import * as rpc from '../common/plugin-api-rpc'; import { @@ -108,10 +108,6 @@ export function fromSelection(selection: types.Selection): Selection { } export function toRange(range: model.Range): types.Range { - // if (!range) { - // return undefined; - // } - const { startLineNumber, startColumn, endLineNumber, endColumn } = range; return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1); } @@ -401,6 +397,16 @@ export function fromLocation(location: theia.Location): model.Location { }; } +export function fromTextDocumentShowOptions(options: theia.TextDocumentShowOptions): model.TextDocumentShowOptions { + if (options.selection) { + return { + ...options, + selection: fromRange(options.selection), + }; + } + return options as model.TextDocumentShowOptions; +} + export function fromDefinitionLink(definitionLink: theia.DefinitionLink): model.LocationLink { return { uri: definitionLink.targetUri, @@ -640,7 +646,10 @@ export function isModelCallHierarchyItem(thing: any): thing is model.CallHierarc if (!thing) { return false; } - return false; + return isModelRange(thing.range) + && isModelRange(thing.selectionRange) + && isUriComponents(thing.uri) + && !!thing.name; } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -648,7 +657,8 @@ export function isModelCallHierarchyIncomingCall(thing: any): thing is model.Cal if (!thing) { return false; } - return false; + const maybeIncomingCall = thing as model.CallHierarchyIncomingCall; + return 'from' in maybeIncomingCall && 'fromRanges' in maybeIncomingCall && isModelCallHierarchyItem(maybeIncomingCall.from); } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -656,7 +666,8 @@ export function isModelCallHierarchyOutgoingCall(thing: any): thing is model.Cal if (!thing) { return false; } - return false; + const maybeOutgoingCall = thing as model.CallHierarchyOutgoingCall; + return 'to' in maybeOutgoingCall && 'fromRanges' in maybeOutgoingCall && isModelCallHierarchyItem(maybeOutgoingCall.to); } export function toLocation(value: model.Location): types.Location { @@ -964,29 +975,30 @@ export function getShellExecutionOptions(options: theia.ShellExecutionOptions): return result; } -export function fromSymbolInformation(symbolInformation: theia.SymbolInformation): SymbolInformation | undefined { +export function fromSymbolInformation(symbolInformation: theia.SymbolInformation): lstypes.SymbolInformation | undefined { if (!symbolInformation) { return undefined; } if (symbolInformation.location && symbolInformation.location.range) { - const p1 = P.create(symbolInformation.location.range.start.line, symbolInformation.location.range.start.character); - const p2 = P.create(symbolInformation.location.range.end.line, symbolInformation.location.range.end.character); - return SymbolInformation.create(symbolInformation.name, symbolInformation.kind++ as S, R.create(p1, p2), + const p1 = lstypes.Position.create(symbolInformation.location.range.start.line, symbolInformation.location.range.start.character); + const p2 = lstypes.Position.create(symbolInformation.location.range.end.line, symbolInformation.location.range.end.character); + return lstypes.SymbolInformation.create(symbolInformation.name, symbolInformation.kind++ as lstypes.SymbolKind, lstypes.Range.create(p1, p2), symbolInformation.location.uri.toString(), symbolInformation.containerName); } - return { + return { name: symbolInformation.name, containerName: symbolInformation.containerName, - kind: symbolInformation.kind++ as S, + kind: symbolInformation.kind++ as lstypes.SymbolKind, location: { - uri: symbolInformation.location.uri.toString() + uri: symbolInformation.location.uri.toString(), + range: symbolInformation.location.range, } }; } -export function toSymbolInformation(symbolInformation: SymbolInformation): theia.SymbolInformation | undefined { +export function toSymbolInformation(symbolInformation: lstypes.SymbolInformation): theia.SymbolInformation | undefined { if (!symbolInformation) { return undefined; } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 901836f92b242..cb9ea6b2ba05f 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -568,6 +568,22 @@ export class Selection extends Range { } } +export namespace TextDocumentShowOptions { + /** + * @param candidate + * @returns `true` if `candidate` is an instance of options that includes a selection. + * This function should be used to determine whether TextDocumentOptions passed into commands by plugins + * need to be translated to TextDocumentShowOptions in the style of the RPC model. Selection is the only field that requires translation. + */ + export function isTextDocumentShowOptions(candidate: unknown): candidate is theia.TextDocumentShowOptions { + if (!candidate) { + return false; + } + const options = candidate as theia.TextDocumentShowOptions; + return Range.isRange(options.selection); + } +} + export enum EndOfLine { LF = 1, CRLF = 2 diff --git a/packages/timeline/src/browser/timeline-widget.tsx b/packages/timeline/src/browser/timeline-widget.tsx index fa4db42026604..8781de076832b 100644 --- a/packages/timeline/src/browser/timeline-widget.tsx +++ b/packages/timeline/src/browser/timeline-widget.tsx @@ -68,14 +68,14 @@ export class TimelineWidget extends BaseWidget { this.refresh(); this.toDispose.push(this.timelineService.onDidChangeTimeline(event => { - const currentWidgetUri = this.getCurrentWidgetUri(); - if (currentWidgetUri ) { - this.loadTimeline(currentWidgetUri, event.reset); - } - }) + const currentWidgetUri = this.getCurrentWidgetUri(); + if (currentWidgetUri) { + this.loadTimeline(currentWidgetUri, event.reset); + } + }) ); this.toDispose.push(this.selectionService.onSelectionChanged(selection => { - if (Array.isArray(selection) && 'uri' in selection[0]) { + if (Array.isArray(selection) && !!selection[0] && 'uri' in selection[0]) { this.refresh(selection[0].uri); } })); @@ -155,7 +155,7 @@ export class TimelineWidget extends BaseWidget { } }); } - return NavigatableWidget.is(current) ? current.getResourceUri() : undefined; + return NavigatableWidget.is(current) ? current.getResourceUri() : undefined; } protected get containerLayout(): PanelLayout | undefined {