Skip to content

Commit

Permalink
GH-707: Introduced the @theia/typehierarchy extension.
Browse files Browse the repository at this point in the history
 - Extended the LSP with the type hierarchy.
 - From now on, a tree node can carry decoration data.
 - Added editor access for the current/active editors.

Closes: #707.

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Mar 6, 2019
1 parent 184ce8d commit 79ffbdf
Show file tree
Hide file tree
Showing 24 changed files with 1,214 additions and 12 deletions.
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

0 comments on commit 79ffbdf

Please sign in to comment.