Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/plugin-ext/src/api/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,16 @@ export interface RenameProvider {
provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string): PromiseLike<WorkspaceEdit & Rejection>;
resolveRenameLocation?(model: monaco.editor.ITextModel, position: Position): PromiseLike<RenameLocation & Rejection>;
}

export interface CallHierarchyDefinition {
location: Location,
symbolName: string,
symbolKind: SymbolKind,
containerName: string,
callers: CallHierarchyCaller[] | undefined
}

export interface CallHierarchyCaller {
callerDefinition: CallHierarchyDefinition,
references: Location[]
}
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/api/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import {
Breakpoint,
ColorPresentation,
RenameLocation,
CallHierarchyDefinition,
CallHierarchyCaller
} from './model';
import { ExtPluginApi } from '../common/plugin-ext-api-contribution';
import { KeysToAnyValues, KeysToKeysToAnyValue } from '../common/types';
Expand Down Expand Up @@ -872,6 +874,8 @@ export interface LanguagesExt {
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: RawColorInfo): PromiseLike<ColorPresentation[]>;
$provideRenameEdits(handle: number, resource: UriComponents, position: Position, newName: string): PromiseLike<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: Position): PromiseLike<RenameLocation | undefined>;
$provideRootDefinition(handle: number, resource: UriComponents, location: Location): Promise<CallHierarchyDefinition | undefined>;
$provideCallers(handle: number, definition: CallHierarchyDefinition): Promise<CallHierarchyCaller[] | undefined>;
}

export interface LanguagesMain {
Expand Down Expand Up @@ -900,6 +904,7 @@ export interface LanguagesMain {
$registerFoldingRangeProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerDocumentColorProvider(handle: number, selector: SerializedDocumentFilter[]): void;
$registerRenameProvider(handle: number, selector: SerializedDocumentFilter[], supportsResoveInitialValues: boolean): void;
$registerCallHierarchyProvider(handle: number, selector: SerializedDocumentFilter[]): void;
}

export interface WebviewPanelViewState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/********************************************************************************
* Copyright (C) 2019 Red Hat, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* 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 * as model from '../../../api/model';
import * as callhierarchy from 'vscode-languageserver-types';
import URI from 'vscode-uri';
import { UriComponents } from '../../../common/uri-components';
import { Location } from 'vscode-languageserver-types';

export function toUriComponents(uri: string): UriComponents {
return URI.parse(uri);
}

export function fromUriComponents(uri: UriComponents): string {
return URI.revive(uri).toString();
}

export function fromLocation (location: Location): model.Location {
return <model.Location> {
uri: URI.parse(location.uri),
range: fromRange(location.range)
};
}

export function toLocation (location: model.Location): Location {
return <Location> {
uri: URI.revive(location.uri).toString(),
range: toRange(location.range)
};
}

export function fromRange(range: callhierarchy.Range | undefined): model.Range | undefined {
if (!range) {
return undefined;
}
const { start, end } = range;
return {
startLineNumber: start.line + 1,
startColumn: start.character + 1,
endLineNumber: end.line + 1,
endColumn: end.character + 1
};
}

export function toRange(range: model.Range | undefined): callhierarchy.Range | undefined {
if (!range) {
return undefined;
}

return callhierarchy.Range.create(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
}

export function toReferences(items: model.Location[]): Location[] {
return items.map(toLocation);
}

export namespace SymbolKindConverter {
// tslint:disable-next-line:no-null-keyword
const fromMapping: { [kind: number]: model.SymbolKind } = Object.create(null);
fromMapping[callhierarchy.SymbolKind.File] = model.SymbolKind.File;
fromMapping[callhierarchy.SymbolKind.Module] = model.SymbolKind.Module;
fromMapping[callhierarchy.SymbolKind.Namespace] = model.SymbolKind.Namespace;
fromMapping[callhierarchy.SymbolKind.Package] = model.SymbolKind.Package;
fromMapping[callhierarchy.SymbolKind.Class] = model.SymbolKind.Class;
fromMapping[callhierarchy.SymbolKind.Method] = model.SymbolKind.Method;
fromMapping[callhierarchy.SymbolKind.Property] = model.SymbolKind.Property;
fromMapping[callhierarchy.SymbolKind.Field] = model.SymbolKind.Field;
fromMapping[callhierarchy.SymbolKind.Constructor] = model.SymbolKind.Constructor;
fromMapping[callhierarchy.SymbolKind.Enum] = model.SymbolKind.Enum;
fromMapping[callhierarchy.SymbolKind.Interface] = model.SymbolKind.Interface;
fromMapping[callhierarchy.SymbolKind.Function] = model.SymbolKind.Function;
fromMapping[callhierarchy.SymbolKind.Variable] = model.SymbolKind.Variable;
fromMapping[callhierarchy.SymbolKind.Constant] = model.SymbolKind.Constant;
fromMapping[callhierarchy.SymbolKind.String] = model.SymbolKind.String;
fromMapping[callhierarchy.SymbolKind.Number] = model.SymbolKind.Number;
fromMapping[callhierarchy.SymbolKind.Boolean] = model.SymbolKind.Boolean;
fromMapping[callhierarchy.SymbolKind.Array] = model.SymbolKind.Array;
fromMapping[callhierarchy.SymbolKind.Object] = model.SymbolKind.Object;
fromMapping[callhierarchy.SymbolKind.Key] = model.SymbolKind.Key;
fromMapping[callhierarchy.SymbolKind.Null] = model.SymbolKind.Null;
fromMapping[callhierarchy.SymbolKind.EnumMember] = model.SymbolKind.EnumMember;
fromMapping[callhierarchy.SymbolKind.Struct] = model.SymbolKind.Struct;
fromMapping[callhierarchy.SymbolKind.Event] = model.SymbolKind.Event;
fromMapping[callhierarchy.SymbolKind.Operator] = model.SymbolKind.Operator;
fromMapping[callhierarchy.SymbolKind.TypeParameter] = model.SymbolKind.TypeParameter;

export function fromSymbolKind(kind: callhierarchy.SymbolKind): model.SymbolKind {
return fromMapping[kind] || model.SymbolKind.Property;
}

// tslint:disable-next-line:no-null-keyword
const toMapping: { [kind: number]: callhierarchy.SymbolKind } = Object.create(null);
toMapping[model.SymbolKind.File] = callhierarchy.SymbolKind.File;
toMapping[model.SymbolKind.Module] = callhierarchy.SymbolKind.Module;
toMapping[model.SymbolKind.Namespace] = callhierarchy.SymbolKind.Namespace;
toMapping[model.SymbolKind.Package] = callhierarchy.SymbolKind.Package;
toMapping[model.SymbolKind.Class] = callhierarchy.SymbolKind.Class;
toMapping[model.SymbolKind.Method] = callhierarchy.SymbolKind.Method;
toMapping[model.SymbolKind.Property] = callhierarchy.SymbolKind.Property;
toMapping[model.SymbolKind.Field] = callhierarchy.SymbolKind.Field;
toMapping[model.SymbolKind.Constructor] = callhierarchy.SymbolKind.Constructor;
toMapping[model.SymbolKind.Enum] = callhierarchy.SymbolKind.Enum;
toMapping[model.SymbolKind.Interface] = callhierarchy.SymbolKind.Interface;
toMapping[model.SymbolKind.Function] = callhierarchy.SymbolKind.Function;
toMapping[model.SymbolKind.Variable] = callhierarchy.SymbolKind.Variable;
toMapping[model.SymbolKind.Constant] = callhierarchy.SymbolKind.Constant;
toMapping[model.SymbolKind.String] = callhierarchy.SymbolKind.String;
toMapping[model.SymbolKind.Number] = callhierarchy.SymbolKind.Number;
toMapping[model.SymbolKind.Boolean] = callhierarchy.SymbolKind.Boolean;
toMapping[model.SymbolKind.Array] = callhierarchy.SymbolKind.Array;
toMapping[model.SymbolKind.Object] = callhierarchy.SymbolKind.Object;
toMapping[model.SymbolKind.Key] = callhierarchy.SymbolKind.Key;
toMapping[model.SymbolKind.Null] = callhierarchy.SymbolKind.Null;
toMapping[model.SymbolKind.EnumMember] = callhierarchy.SymbolKind.EnumMember;
toMapping[model.SymbolKind.Struct] = callhierarchy.SymbolKind.Struct;
toMapping[model.SymbolKind.Event] = callhierarchy.SymbolKind.Event;
toMapping[model.SymbolKind.Operator] = callhierarchy.SymbolKind.Operator;
toMapping[model.SymbolKind.TypeParameter] = callhierarchy.SymbolKind.TypeParameter;

export function toSymbolKind(kind: model.SymbolKind): callhierarchy.SymbolKind {
return toMapping[kind] || model.SymbolKind.Property;
}
}

export function toDefinition(definition: model.CallHierarchyDefinition | undefined): CallHierarchyDefinition | undefined {
if (!definition) {
return undefined;
}

return <CallHierarchyDefinition> {
location: toLocation(definition.location),
symbolName: definition.symbolName,
symbolKind: SymbolKindConverter.toSymbolKind(definition.symbolKind),
containerName: definition.containerName,
callers: (definition.callers) ? definition.callers.map(toCaller) : undefined
};
}

export function fromDefinition(definition: CallHierarchyDefinition): model.CallHierarchyDefinition {
return <model.CallHierarchyDefinition> {
location: fromLocation(definition.location),
symbolName: definition.symbolName,
symbolKind: SymbolKindConverter.fromSymbolKind(definition.symbolKind),
containerName: definition.containerName,
callers: (definition.callers) ? definition.callers.map(fromCaller) : undefined
};
}

export function toCaller(caller: model.CallHierarchyCaller): CallHierarchyCaller {
return <CallHierarchyCaller> {
callerDefinition: toDefinition(caller.callerDefinition),
references: caller.references.map(toLocation)
};
}

export function fromCaller(caller: CallHierarchyCaller): model.CallHierarchyCaller {
return <model.CallHierarchyCaller> {
callerDefinition: fromDefinition(caller.callerDefinition),
references: caller.references.map(fromLocation)
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/********************************************************************************
* Copyright (C) 2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, postConstruct } from 'inversify';
import { CallHierarchyService, CallHierarchyServiceProvider } from '@theia/callhierarchy/lib/browser';
import { Disposable } from '@theia/core/lib/common/disposable';

/**
* Plugin Call Hierarchy Service contribution registrator.
*/
export interface PluginCallHierarchyServiceContributionRegistrator {
/**
* Registers [CallHierarchyService] contribution.
* @param service service
*/
registerCallHierarchyServiceContribution(service: CallHierarchyService): Disposable;

/**
* Unregisters [CallHierarchyService] contribution.
* @param languageId the language ID
*/
unregisterCallHierarchyServiceContribution(languageId: string): void;
}

@injectable()
export class PluginCallHierarchyServiceProvider extends CallHierarchyServiceProvider implements PluginCallHierarchyServiceContributionRegistrator {
protected readonly services = new Map<string, CallHierarchyService>();

@postConstruct()
protected init(): void {
for (const contrib of this.contributions.getContributions()) {
this.services.set(contrib.languageId, contrib);
}
}

get(languageId: string): CallHierarchyService | undefined {
return this.services.get(languageId);
}

registerCallHierarchyServiceContribution(service: CallHierarchyService): Disposable {
const { languageId } = service;

if (this.services.has(languageId)) {
console.warn(`Call Hierarchy Service contribution is already registered for ${languageId}`);
return Disposable.NULL;
}

this.services.set(languageId, service);
return Disposable.create(() => this.unregisterCallHierarchyServiceContribution(languageId));
}

unregisterCallHierarchyServiceContribution(languageId: string): void {
this.services.delete(languageId);
}
}
48 changes: 47 additions & 1 deletion packages/plugin-ext/src/main/browser/languages-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
ResourceTextEditDto,
ResourceFileEditDto,
} from '../../api/plugin-api';
import { interfaces } from 'inversify';
import { inject, interfaces } from 'inversify';
import { SerializedDocumentFilter, MarkerData, Range, WorkspaceSymbolProvider } from '../../api/model';
import { RPCProtocol } from '../../api/rpc-protocol';
import { fromLanguageSelector } from '../../plugin/type-converters';
Expand All @@ -37,10 +37,17 @@ import { DisposableCollection, Emitter } from '@theia/core';
import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages';
import URI from 'vscode-uri/lib/umd';

import { CallHierarchyService, Caller } from '@theia/callhierarchy/lib/browser';
import { fromDefinition, toDefinition, toCaller, toUriComponents, fromLocation } from './callhierarchy/callhierarchy-type-converters';
import { PluginCallHierarchyServiceProvider } from '../browser/callhierarchy/plugin-callhierarchy-service-provider';

export class LanguagesMainImpl implements LanguagesMain {

private ml: MonacoLanguages;

@inject(PluginCallHierarchyServiceProvider)
private readonly callHierarchyServiceContributionRegistry: PluginCallHierarchyServiceProvider;

private readonly proxy: LanguagesExt;
private readonly disposables = new Map<number, monaco.IDisposable>();
constructor(rpc: RPCProtocol, container: interfaces.Container) {
Expand Down Expand Up @@ -666,6 +673,45 @@ export class LanguagesMainImpl implements LanguagesMain {
};
}

$registerCallHierarchyProvider(handle: number, selector: SerializedDocumentFilter[]): void {
const languageSelector = fromLanguageSelector(selector);
const disposable = new DisposableCollection();
for (const language of getLanguages()) {
if (this.matchLanguage(languageSelector, language)) {
const callHierarchyService = this.createCallHierarchyService(handle, language);
disposable.push(
this.callHierarchyServiceContributionRegistry
.registerCallHierarchyServiceContribution(callHierarchyService));
}
}
this.disposables.set(handle, disposable);
}

protected createCallHierarchyService(handle: number, language: string): CallHierarchyService {
return {
languageId: language,
getRootDefinition: location =>
this.proxy.$provideRootDefinition(handle, toUriComponents(location.uri), fromLocation(location))
.then(def => toDefinition(def)),
getCallers: definition => this.proxy.$provideCallers(handle, fromDefinition(definition))
.then(result => {
if (!result) {
return undefined!;
}

if (Array.isArray(result)) {
const callers: Caller[] = [];
for (const item of result) {
callers.push(toCaller(item));
}
return callers;
}

return undefined!;
})
};
}

protected matchModel(selector: LanguageSelector | undefined, model: MonacoModelIdentifier): boolean {
if (Array.isArray(selector)) {
return selector.some(filter => this.matchModel(filter, model));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import { DebugSessionContributionRegistry } from '@theia/debug/lib/browser/debug
import { PluginDebugSessionContributionRegistry } from './debug/plugin-debug-session-contribution-registry';
import { PluginDebugService } from './debug/plugin-debug-service';
import { DebugService } from '@theia/debug/lib/common/debug-service';
import { CallHierarchyServiceProvider } from '@theia/callhierarchy/lib/browser';
import { PluginCallHierarchyServiceProvider } from './callhierarchy/plugin-callhierarchy-service-provider';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bindHostedPluginPreferences(bind);
Expand Down Expand Up @@ -131,4 +133,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
rebind(DebugService).toService(PluginDebugService);
bind(PluginDebugSessionContributionRegistry).toSelf().inSingletonScope();
rebind(DebugSessionContributionRegistry).toService(PluginDebugSessionContributionRegistry);

bind(PluginCallHierarchyServiceProvider).toSelf().inSingletonScope();
rebind(CallHierarchyServiceProvider).toService(PluginCallHierarchyServiceProvider);
});
Loading