Skip to content

GH-707: Introduced the @theia/typehierarchy extension. #3802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 7, 2019
Merged
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ cache:
- packages/terminal/node_modules
- packages/textmate-grammars/node_modules
- packages/tslint/node_modules
- packages/typehierarchy/node_modules
- packages/typescript/node_modules
- packages/userstorage/node_modules
- packages/variable-resolver/node_modules
Expand Down
1 change: 1 addition & 0 deletions examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@theia/terminal": "^0.4.0",
"@theia/textmate-grammars": "^0.4.0",
"@theia/tslint": "^0.4.0",
"@theia/typehierarchy": "^0.4.0",
"@theia/typescript": "^0.4.0",
"@theia/userstorage": "^0.4.0",
"@theia/variable-resolver": "^0.4.0",
Expand Down
1 change: 1 addition & 0 deletions examples/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@theia/terminal": "^0.4.0",
"@theia/textmate-grammars": "^0.4.0",
"@theia/tslint": "^0.4.0",
"@theia/typehierarchy": "^0.4.0",
"@theia/typescript": "^0.4.0",
"@theia/userstorage": "^0.4.0",
"@theia/variable-resolver": "^0.4.0",
Expand Down
25 changes: 24 additions & 1 deletion packages/core/src/browser/tree/tree-decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import { injectable } from 'inversify';
import { Tree } from './tree';
import { Tree, TreeNode } from './tree';
import { Event, Emitter, Disposable, DisposableCollection, MaybePromise } from '../../common';

/**
Expand Down Expand Up @@ -501,4 +501,27 @@ export namespace TreeDecoration {

}

/**
* Tree node that can be decorated explicitly, without the tree decorators.
*/
export interface DecoratedTreeNode extends TreeNode {

/**
* The additional tree decoration data attached to the tree node itself.
*/
readonly decorationData: Data;

}

export namespace DecoratedTreeNode {

/**
* Type-guard for decorated tree nodes.
*/
export function is(node: TreeNode | undefined): node is DecoratedTreeNode {
return !!node && 'decorationData' in node;
}

}

}
11 changes: 7 additions & 4 deletions packages/core/src/browser/tree/tree-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -617,11 +617,14 @@ export class TreeWidget extends ReactWidget implements StatefulWidget {
}

protected getDecorations(node: TreeNode): TreeDecoration.Data[] {
const decorations = this.decorations.get(node.id);
if (decorations) {
return decorations.sort(TreeDecoration.Data.comparePriority);
const decorations: TreeDecoration.Data[] = [];
if (TreeDecoration.DecoratedTreeNode.is(node)) {
decorations.push(node.decorationData);
}
if (this.decorations.has(node.id)) {
decorations.push(...this.decorations.get(node.id));
}
return [];
return decorations.sort(TreeDecoration.Data.comparePriority);
}

protected getDecorationData<K extends keyof TreeDecoration.Data>(node: TreeNode, key: K): TreeDecoration.Data[K][] {
Expand Down
7 changes: 6 additions & 1 deletion packages/editor/src/browser/editor-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ContainerModule } from 'inversify';
import { CommandContribution, MenuContribution } from '@theia/core/lib/common';
import { OpenHandler, WidgetFactory, FrontendApplicationContribution, KeybindingContext, KeybindingContribution } from '@theia/core/lib/browser';
import { VariableContribution } from '@theia/variable-resolver/lib/browser';
import { EditorManager } from './editor-manager';
import { EditorManager, EditorAccess, ActiveEditorAccess, CurrentEditorAccess } from './editor-manager';
import { EditorContribution } from './editor-contribution';
import { EditorMenuContribution } from './editor-menu';
import { EditorCommandContribution } from './editor-command';
Expand Down Expand Up @@ -62,4 +62,9 @@ export default new ContainerModule(bind => {
bind(VariableContribution).to(EditorVariableContribution).inSingletonScope();

bind(SemanticHighlightingService).toSelf().inSingletonScope();

bind(CurrentEditorAccess).toSelf().inSingletonScope();
bind(ActiveEditorAccess).toSelf().inSingletonScope();
bind(EditorAccess).to(CurrentEditorAccess).inSingletonScope().whenTargetNamed(EditorAccess.CURRENT);
bind(EditorAccess).to(ActiveEditorAccess).inSingletonScope().whenTargetNamed(EditorAccess.ACTIVE);
});
99 changes: 97 additions & 2 deletions packages/editor/src/browser/editor-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable, postConstruct, } from 'inversify';
import { injectable, postConstruct, inject } from 'inversify';
import URI from '@theia/core/lib/common/uri';
import { RecursivePartial, Emitter, Event } from '@theia/core/lib/common';
import { WidgetOpenerOptions, NavigatableWidgetOpenHandler } from '@theia/core/lib/browser';
import { EditorWidget } from './editor-widget';
import { Range, Position } from './editor';
import { Range, Position, Location } from './editor';
import { EditorWidgetFactory } from './editor-widget-factory';
import { TextEditor } from './editor';

export interface EditorOpenerOptions extends WidgetOpenerOptions {
selection?: RecursivePartial<Range>;
Expand Down Expand Up @@ -139,3 +140,97 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
}

}

/**
* Provides direct access to the underlying text editor.
*/
@injectable()
export abstract class EditorAccess {

@inject(EditorManager)
protected readonly editorManager: EditorManager;

/**
* The URI of the underlying document from the editor.
*/
get uri(): string | undefined {
const editor = this.editor;
if (editor) {
return editor.uri.toString();
}
return undefined;
}

/**
* The selection location from the text editor.
*/
get selection(): Location | undefined {
const editor = this.editor;
if (editor) {
const uri = editor.uri.toString();
const range = editor.selection;
return {
range,
uri
};
}
return undefined;
}

/**
* The unique identifier of the language the current editor belongs to.
*/
get languageId(): string | undefined {
const editor = this.editor;
if (editor) {
return editor.document.languageId;
}
return undefined;
}

/**
* The text editor.
*/
get editor(): TextEditor | undefined {
const editorWidget = this.editorWidget();
if (editorWidget) {
return editorWidget.editor;
}
return undefined;
}

/**
* The editor widget, or `undefined` if not applicable.
*/
protected abstract editorWidget(): EditorWidget | undefined;

}

/**
* Provides direct access to the currently active text editor.
*/
@injectable()
export class CurrentEditorAccess extends EditorAccess {

protected editorWidget(): EditorWidget | undefined {
return this.editorManager.currentEditor;
}

}

/**
* Provides access to the active text editor.
*/
@injectable()
export class ActiveEditorAccess extends EditorAccess {

protected editorWidget(): EditorWidget | undefined {
return this.editorManager.activeEditor;
}

}

export namespace EditorAccess {
export const CURRENT = 'current-editor-access';
export const ACTIVE = 'active-editor-access';
}
4 changes: 2 additions & 2 deletions packages/editor/src/browser/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { Position, Range } from 'vscode-languageserver-types';
import { Position, Range, Location } from 'vscode-languageserver-types';
import * as lsp from 'vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
import { Event, Disposable } from '@theia/core/lib/common';
import { Saveable } from '@theia/core/lib/browser';
import { EditorDecoration } from './decorations';

export {
Position, Range
Position, Range, Location
};

export const TextEditorProvider = Symbol('TextEditorProvider');
Expand Down
7 changes: 5 additions & 2 deletions packages/languages/src/browser/language-client-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
ILanguageClient, LanguageClientOptions, MonacoLanguageClient,
createConnection, LanguageContribution
} from './language-client-services';
import { TypeHierarchyFeature } from './typehierarchy/typehierarchy-feature';

@injectable()
export class LanguageClientFactory {
Expand Down Expand Up @@ -64,7 +65,7 @@ export class LanguageClientFactory {
}
const initializationFailedHandler = clientOptions.initializationFailedHandler;
clientOptions.initializationFailedHandler = e => !!initializationFailedHandler && initializationFailedHandler(e);
return this.patch4085(new MonacoLanguageClient({
const client = new MonacoLanguageClient({
id: contribution.id,
name: contribution.name,
clientOptions,
Expand All @@ -74,7 +75,9 @@ export class LanguageClientFactory {
return createConnection(connection, errorHandler, closeHandler);
}
}
}));
});
client.registerFeature(new TypeHierarchyFeature(client));
return this.patch4085(client);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/********************************************************************************
* 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 { v4 } from 'uuid';
import {
Disposable,
ILanguageClient,
DocumentSelector,
ClientCapabilities,
ServerCapabilities,
TextDocumentFeature,
TextDocumentRegistrationOptions
} from '../language-client-services';
import { TypeHierarchyRequest } from './typehierarchy-protocol';

// NOTE: This module can be removed, or at least can be simplified once the type hierarchy will become the part of the LSP.
// https://github.com/Microsoft/language-server-protocol/issues/582
// https://github.com/Microsoft/vscode-languageserver-node/pull/346#discussion_r221659062

/**
* Text document feature for handling super- and subtype hierarchies through the LSP.
*/
export class TypeHierarchyFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {

constructor(readonly client: ILanguageClient) {
super(client, TypeHierarchyRequest.type);
}

fillClientCapabilities(capabilities: ClientCapabilities): void {
if (!capabilities.textDocument) {
capabilities.textDocument = {};
}
// tslint:disable-next-line:no-any
(capabilities.textDocument as any).typeHierarchy = {
dynamicRegistration: true
};
}

initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
if (!documentSelector) {
return;
}
const capabilitiesExt: ServerCapabilities & { typeHierarchyProvider?: boolean } = capabilities;
if (capabilitiesExt.typeHierarchyProvider) {
const id = v4();
this.register(this.messages, {
id,
registerOptions: Object.assign({}, { documentSelector: documentSelector }, capabilitiesExt.typeHierarchyProvider)
});
}
}

protected registerLanguageProvider(): Disposable {
return Disposable.create(() => { /* NOOP */ });
}

}
Loading