diff --git a/.travis.yml b/.travis.yml index 391e3cec47554..c6c6385b7140f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/examples/browser/package.json b/examples/browser/package.json index b20b73cbe073b..a55d22aed55cf 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -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", diff --git a/examples/electron/package.json b/examples/electron/package.json index 3b733431f07a6..d24d36c8c21bd 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -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", diff --git a/packages/core/src/browser/tree/tree-decorator.ts b/packages/core/src/browser/tree/tree-decorator.ts index 603a918eee45a..41f780a96c381 100644 --- a/packages/core/src/browser/tree/tree-decorator.ts +++ b/packages/core/src/browser/tree/tree-decorator.ts @@ -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'; /** @@ -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; + } + + } + } diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index 6a6bd2ad22eb7..c29001f68d683 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -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(node: TreeNode, key: K): TreeDecoration.Data[K][] { diff --git a/packages/editor/src/browser/editor-frontend-module.ts b/packages/editor/src/browser/editor-frontend-module.ts index b96527c1bde41..effa2a9c00cec 100644 --- a/packages/editor/src/browser/editor-frontend-module.ts +++ b/packages/editor/src/browser/editor-frontend-module.ts @@ -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'; @@ -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); }); diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 2b5e515e0f2a5..2d29c98e1b8bd 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -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; @@ -139,3 +140,97 @@ export class EditorManager extends NavigatableWidgetOpenHandler { } } + +/** + * 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'; +} diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index 8629ba6b941d2..ae69ee6135963 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -14,7 +14,7 @@ * 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'; @@ -22,7 +22,7 @@ import { Saveable } from '@theia/core/lib/browser'; import { EditorDecoration } from './decorations'; export { - Position, Range + Position, Range, Location }; export const TextEditorProvider = Symbol('TextEditorProvider'); diff --git a/packages/languages/src/browser/language-client-factory.ts b/packages/languages/src/browser/language-client-factory.ts index d59a63bc34330..a435c11b9f750 100644 --- a/packages/languages/src/browser/language-client-factory.ts +++ b/packages/languages/src/browser/language-client-factory.ts @@ -23,6 +23,7 @@ import { ILanguageClient, LanguageClientOptions, MonacoLanguageClient, createConnection, LanguageContribution } from './language-client-services'; +import { TypeHierarchyFeature } from './typehierarchy/typehierarchy-feature'; @injectable() export class LanguageClientFactory { @@ -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, @@ -74,7 +75,9 @@ export class LanguageClientFactory { return createConnection(connection, errorHandler, closeHandler); } } - })); + }); + client.registerFeature(new TypeHierarchyFeature(client)); + return this.patch4085(client); } /** diff --git a/packages/languages/src/browser/typehierarchy/typehierarchy-feature.ts b/packages/languages/src/browser/typehierarchy/typehierarchy-feature.ts new file mode 100644 index 0000000000000..5d0f46fd46f6b --- /dev/null +++ b/packages/languages/src/browser/typehierarchy/typehierarchy-feature.ts @@ -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 { + + 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 */ }); + } + +} diff --git a/packages/languages/src/browser/typehierarchy/typehierarchy-protocol.ts b/packages/languages/src/browser/typehierarchy/typehierarchy-protocol.ts new file mode 100644 index 0000000000000..db03b21c3509b --- /dev/null +++ b/packages/languages/src/browser/typehierarchy/typehierarchy-protocol.ts @@ -0,0 +1,198 @@ +/******************************************************************************** + * 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 { RequestType } from 'vscode-jsonrpc'; +import { SymbolKind, Range } from 'vscode-languageserver-types'; +import { TextDocumentPositionParams, TextDocumentRegistrationOptions, StaticRegistrationOptions } from 'vscode-languageserver'; + +// NOTE: This module can be removed, 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 + +/** + * Client capabilities specific to the type hierarchy feature. + */ +export interface TypeHierarchyCapabilities { + + /** + * The text document client capabilities. + */ + textDocument?: { + + /** + * Capabilities specific to the `textDocument/typeHierarchy`. + */ + typeHierarchy?: { + + /** + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` + * return value for the corresponding server capability as well. + */ + dynamicRegistration?: boolean; + + } + + } + +} + +/** + * Type hierarchy language server capability. + */ +export interface TypeHierarchyServerCapabilities { + + /** + * Server capability for calculating super- and subtype hierarchies. + */ + typeHierarchyProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); + +} + +/** + * The type hierarchy params is an extension of the `TextDocumentPositionParams` with optional properties + * which can be used to eagerly resolve the item when requesting from the server. + */ +export interface TypeHierarchyParams extends TextDocumentPositionParams { + + /** + * The hierarchy levels to resolve. `0` indicates no level. When not defined, it is treated as `0`. + */ + resolve?: number; + + /** + * The direction of the hierarchy levels to resolve. + */ + direction?: TypeHierarchyDirection + +} + +export namespace TypeHierarchyDirection { + + /** + * Flag for retrieving/resolving the subtypes. + */ + export const Children = 0; + + /** + * Flag to use when retrieving/resolving the supertypes. + */ + export const Parents = 1; + + /** + * Flag for resolving both the super- and subtypes. + */ + export const Both = 2; + +} +export type TypeHierarchyDirection = 0 | 1 | 2; + +/** + * The `textDocument/typeHierarchy` request is sent from the client to the server to retrieve the type hierarchy + * items from a given position of a text document. Can resolve the parentage information on demand. + * If no item can be retrieved for a given text document position, returns with `null`. + */ +export namespace TypeHierarchyRequest { + export const type = new RequestType('textDocument/typeHierarchy'); +} + +/** + * Parameters for the `typeHierarchy/resolve` request. + */ +export interface ResolveTypeHierarchyItemParams { + + /** + * The item to resolve. + */ + item: TypeHierarchyItem; + + /** + * The hierarchy levels to resolve. `0` indicates no level. + */ + resolve: number; + + /** + * The direction of the hierarchy levels to resolve. + */ + direction: TypeHierarchyDirection; +} + +/** + * The `typeHierarchy/resolve` request is sent from the client to the server to resolve a type hierarchy + * item by resolving sub- and supertype information. + */ +export namespace ResolveTypeHierarchyRequest { + export const type = new RequestType('typeHierarchy/resolve'); +} + +export interface TypeHierarchyItem { + + /** + * The human readable name of the hierarchy item. + */ + name: string; + + /** + * Optional detail for the hierarchy item. It can be, for instance, the signature of a function or method. + */ + detail?: string; + + /** + * The kind of the hierarchy item. For instance, class or interface. + */ + kind: SymbolKind; + + /** + * `true` if the hierarchy item is deprecated. Otherwise, `false`. It is `false` by default. + */ + deprecated?: boolean; + + /** + * The URI of the text document where this type hierarchy item belongs to. + */ + uri: string; + + /** + * The range enclosing this type hierarchy item not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the clients cursor is inside the type + * hierarchy item to reveal in the symbol in the UI. + */ + range: Range; + + /** + * The range that should be selected and revealed when this type hierarchy item is being picked, e.g the name + * of a function. Must be contained by the `range`. + */ + selectionRange: Range; + + /** + * If this type hierarchy item is resolved, it contains the direct parents. Could be empty if the item does + * not have any direct parents. If not defined, the parents have not been resolved yet. + */ + parents?: TypeHierarchyItem[]; + + /** + * If this type hierarchy item is resolved, it contains the direct children of the current item. Could be + * empty if the item does not have any descendants. If not defined, the children have not been resolved. + */ + children?: TypeHierarchyItem[]; + + /** + * An optional data field can be used to identify a type hierarchy item in a resolve request. + */ + // tslint:disable-next-line:no-any + data?: any; +} diff --git a/packages/typehierarchy/README.md b/packages/typehierarchy/README.md new file mode 100644 index 0000000000000..7b7130f2bde8b --- /dev/null +++ b/packages/typehierarchy/README.md @@ -0,0 +1,8 @@ +# Theia - Type Hierarchy Extension + +Supports super- and subtype information for document symbols. + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) diff --git a/packages/typehierarchy/compile.tsconfig.json b/packages/typehierarchy/compile.tsconfig.json new file mode 100644 index 0000000000000..18b90d15df3c5 --- /dev/null +++ b/packages/typehierarchy/compile.tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../configs/base.tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ] +} diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json new file mode 100644 index 0000000000000..d15a74a2cae23 --- /dev/null +++ b/packages/typehierarchy/package.json @@ -0,0 +1,50 @@ +{ + "name": "@theia/typehierarchy", + "version": "0.4.0", + "description": "Theia - Type Hierarchy Extension", + "dependencies": { + "@theia/core": "^0.4.0", + "@theia/editor": "^0.4.0", + "@theia/languages": "^0.4.0", + "@types/uuid": "^3.4.3", + "uuid": "^3.2.1" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/typehierarchy-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/theia-ide/theia.git" + }, + "bugs": { + "url": "https://github.com/theia-ide/theia/issues" + }, + "homepage": "https://github.com/theia-ide/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "prepare": "yarn run clean && yarn run build", + "clean": "theiaext clean", + "build": "theiaext build", + "watch": "theiaext watch", + "test": "theiaext test", + "docs": "theiaext docs" + }, + "devDependencies": { + "@theia/ext-scripts": "^0.4.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/typehierarchy/src/browser/style/index.css b/packages/typehierarchy/src/browser/style/index.css new file mode 100644 index 0000000000000..c2a27db892e6f --- /dev/null +++ b/packages/typehierarchy/src/browser/style/index.css @@ -0,0 +1,29 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +.theia-type-hierarchy-tree { + font-size: var(--theia-ui-font-size0); + color: var(--theia-ui-font-color1); +} + +.theia-caption-suffix { + padding-left: 10px !important; +} + +.theia-caption-prefix { + padding-right: 5px !important; + padding-left: 1px !important; +} diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree-container.ts b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-container.ts new file mode 100644 index 0000000000000..d4414687a18ac --- /dev/null +++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-container.ts @@ -0,0 +1,42 @@ +/******************************************************************************** + * 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 { interfaces, Container } from 'inversify'; +import { createTreeContainer, Tree, TreeImpl, TreeModel, TreeModelImpl, TreeWidget } from '@theia/core/lib/browser/tree'; +import { TypeHierarchyTree } from './typehierarchy-tree'; +import { TypeHierarchyTreeModel } from './typehierarchy-tree-model'; +import { TypeHierarchyTreeWidget } from './typehierarchy-tree-widget'; + +function createHierarchyTreeContainer(parent: interfaces.Container): Container { + const child = createTreeContainer(parent); + + child.unbind(TreeImpl); + child.bind(TypeHierarchyTree).toSelf(); + child.rebind(Tree).toService(TypeHierarchyTree); + + child.unbind(TreeModelImpl); + child.bind(TypeHierarchyTreeModel).toSelf(); + child.rebind(TreeModel).toService(TypeHierarchyTreeModel); + + child.bind(TypeHierarchyTreeWidget).toSelf(); + child.rebind(TreeWidget).toService(TypeHierarchyTreeWidget); + + return child; +} + +export function createHierarchyTreeWidget(parent: interfaces.Container): TypeHierarchyTreeWidget { + return createHierarchyTreeContainer(parent).get(TypeHierarchyTreeWidget); +} diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree-model.ts b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-model.ts new file mode 100644 index 0000000000000..ed907f48a55b9 --- /dev/null +++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-model.ts @@ -0,0 +1,84 @@ +/******************************************************************************** + * 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 { inject, injectable } from 'inversify'; +import { TreeNode } from '@theia/core/lib/browser/tree/tree'; +import { TreeModelImpl } from '@theia/core/lib/browser/tree/tree-model'; +import { TypeHierarchyDirection, TypeHierarchyParams } from '@theia/languages/lib/browser/typehierarchy/typehierarchy-protocol'; +import { TypeHierarchyServiceProvider } from '../typehierarchy-service'; +import { TypeHierarchyTree } from './typehierarchy-tree'; + +@injectable() +export class TypeHierarchyTreeModel extends TreeModelImpl { + + @inject(TypeHierarchyServiceProvider) + protected readonly typeHierarchyServiceProvider: TypeHierarchyServiceProvider; + + protected doOpenNode(node: TreeNode): void { + // do nothing (in particular do not expand the node) + } + + /** + * Initializes the tree by calculating and setting a new tree root node. + */ + async initialize(options: TypeHierarchyTree.InitOptions): Promise { + this.tree.root = undefined; + (this.tree as TypeHierarchyTree).service = undefined; + const { location, languageId, direction } = options; + if (languageId && location) { + const service = await this.typeHierarchyServiceProvider.get(languageId); + if (service) { + const params: TypeHierarchyParams = { + textDocument: { + uri: location.uri + }, + position: location.range.start, + direction, + resolve: 1 + }; + const symbol = await service.get(params); + if (symbol) { + const root = TypeHierarchyTree.RootNode.create(symbol, direction); + root.expanded = true; + this.tree.root = root; + (this.tree as TypeHierarchyTree).service = service; + } + } + } + } + + /** + * If the tree root is set, it resets it with the inverse type hierarchy direction. + */ + async flipDirection(): Promise { + const { root } = this.tree; + const service = (this.tree as TypeHierarchyTree).service; + if (TypeHierarchyTree.RootNode.is(root) && !!service) { + const { direction, item } = root; + const { uri, selectionRange } = item; + const location = { + uri, + range: selectionRange + }; + this.initialize({ + direction: direction === TypeHierarchyDirection.Children ? TypeHierarchyDirection.Parents : TypeHierarchyDirection.Children, + location, + languageId: service.languageId + }); + } + } + +} diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree-widget.tsx b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-widget.tsx new file mode 100644 index 0000000000000..3d23d4e6af758 --- /dev/null +++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree-widget.tsx @@ -0,0 +1,105 @@ +/******************************************************************************** + * 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 * as React from 'react'; +import { inject, injectable } from 'inversify'; +import { DockPanel } from '@phosphor/widgets'; +import URI from '@theia/core/lib/common/uri'; +import { SymbolKind, Range } from '@theia/languages/lib/browser'; +import { TreeNode } from '@theia/core/lib/browser/tree/tree'; +import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; +import { ContextMenuRenderer } from '@theia/core/lib/browser/context-menu-renderer'; +import { TreeWidget, TreeProps } from '@theia/core/lib/browser/tree/tree-widget'; +import { TypeHierarchyTreeModel } from './typehierarchy-tree-model'; +import { TypeHierarchyTree } from './typehierarchy-tree'; + +@injectable() +export class TypeHierarchyTreeWidget extends TreeWidget { + + // tslint:disable-next-line:no-any + protected readonly icons = new Map(Array.from(Object.keys(SymbolKind)).map(key => [(SymbolKind as any)[key], key.toLocaleLowerCase()] as [number, string])); + + constructor( + @inject(TreeProps) readonly props: TreeProps, + @inject(TypeHierarchyTreeModel) readonly model: TypeHierarchyTreeModel, + @inject(ContextMenuRenderer) readonly contextMenuRenderer: ContextMenuRenderer, + @inject(EditorManager) readonly editorManager: EditorManager + ) { + super(props, model, contextMenuRenderer); + this.id = TypeHierarchyTreeWidget.WIDGET_ID; + this.title.label = TypeHierarchyTreeWidget.WIDGET_LABEL; + this.title.caption = TypeHierarchyTreeWidget.WIDGET_LABEL; + this.addClass(TypeHierarchyTreeWidget.Styles.TYPE_HIERARCHY_TREE_CLASS); + this.title.closable = true; + this.title.iconClass = 'fa fa-sitemap'; + this.toDispose.push(this.model.onSelectionChanged(selection => { + const node = selection[0]; + if (node) { + this.openEditor(node, true); + } + })); + this.toDispose.push(this.model.onOpenNode(node => this.openEditor(node))); + } + + /** + * Initializes the widget with the new input. + */ + async initialize(options: TypeHierarchyTree.InitOptions): Promise { + await this.model.initialize(options); + } + + /** + * See: `TreeWidget#renderIcon`. + */ + protected renderIcon(node: TreeNode): React.ReactNode { + if (TypeHierarchyTree.Node.is(node)) { + return
; + } + return undefined; + } + + /** + * Opens up the node in the editor. On demand (`keepFocus`) it reveals the location in the editor. + */ + protected async openEditor(node: TreeNode, keepFocus: boolean = false): Promise { + if (TypeHierarchyTree.Node.is(node)) { + const { selectionRange, uri } = node.item; + const editorWidget = await this.editorManager.open(new URI(uri), { + mode: keepFocus ? 'reveal' : 'activate', + selection: Range.create(selectionRange.start, selectionRange.end) + }); + if (editorWidget.parent instanceof DockPanel) { + editorWidget.parent.selectWidget(editorWidget); + } + } + } + +} + +export namespace TypeHierarchyTreeWidget { + + export const WIDGET_ID = 'theia-typehierarchy'; + export const WIDGET_LABEL = 'Type Hierarchy'; + + /** + * CSS styles for the `Type Hierarchy` widget. + */ + export namespace Styles { + + export const TYPE_HIERARCHY_TREE_CLASS = 'theia-type-hierarchy-tree'; + + } +} diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts new file mode 100644 index 0000000000000..6a511a984c50c --- /dev/null +++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts @@ -0,0 +1,188 @@ +/******************************************************************************** + * 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 } from 'inversify'; +import { v4 } from 'uuid'; +import URI from '@theia/core/lib/common/uri'; +import { Location } from '@theia/editor/lib/browser/editor'; +import { TreeDecoration } from '@theia/core/lib/browser/tree/tree-decorator'; +import { TreeImpl, TreeNode, CompositeTreeNode, ExpandableTreeNode, SelectableTreeNode } from '@theia/core/lib/browser/tree'; +import { TypeHierarchyItem } from '@theia/languages/lib/browser/typehierarchy/typehierarchy-protocol'; +import { TypeHierarchyDirection, ResolveTypeHierarchyItemParams } from '@theia/languages/lib/browser/typehierarchy/typehierarchy-protocol'; +import { TypeHierarchyService } from '../typehierarchy-service'; + +@injectable() +export class TypeHierarchyTree extends TreeImpl { + + service: TypeHierarchyService | undefined; + + async resolveChildren(parent: CompositeTreeNode): Promise { + if (TypeHierarchyTree.Node.is(parent)) { + await this.ensureResolved(parent); + if (parent.children.length === 0) { + delete parent.children; + delete parent.expanded; + return []; + } + return parent.children.slice(); + } + return []; + } + + /** + * Returns with the direction of the type hierarchy attached to the root node. `undefined` if the root is not set. + */ + protected get direction(): TypeHierarchyDirection | undefined { + if (TypeHierarchyTree.RootNode.is(this.root)) { + return this.root.direction; + } + return undefined; + } + + /** + * Makes sure, the node and its children are resolved. Resolves it on demand. + */ + protected async ensureResolved(node: TypeHierarchyTree.Node): Promise { + if (!node.resolved) { + const { service, direction } = this; + if (service && direction !== undefined) { + const { item } = node; + const param: ResolveTypeHierarchyItemParams = { + item, + direction, + resolve: 1 + }; + const resolvedItem = await service.resolve(param); + if (resolvedItem) { + node.resolved = true; + const items = TypeHierarchyDirection.Children === direction ? resolvedItem.children : resolvedItem.parents; + if (items) { + node.children = items.map(child => TypeHierarchyTree.Node.create(child, direction, false)); + } else { + node.children = []; + } + } + } + } + } + +} + +export namespace TypeHierarchyTree { + + export interface InitOptions { + readonly direction: TypeHierarchyDirection; + readonly location: Location | undefined; + readonly languageId: string | undefined; + } + + export interface RootNode extends Node { + readonly direction: TypeHierarchyDirection; + } + + export namespace RootNode { + + export function is(node: TreeNode | undefined): node is RootNode { + if (Node.is(node) && 'direction' in node) { + // tslint:disable-next-line:no-any + const { direction } = (node as any); + return direction === TypeHierarchyDirection.Children || direction === TypeHierarchyDirection.Parents; + } + return false; + } + + export function create(item: TypeHierarchyItem, direction: TypeHierarchyDirection): RootNode { + return { + ...Node.create(item, direction, true), + direction + }; + } + + } + + export interface Node extends CompositeTreeNode, ExpandableTreeNode, SelectableTreeNode, TreeDecoration.DecoratedTreeNode { + readonly item: TypeHierarchyItem; + resolved: boolean; + } + + export namespace Node { + + export function is(node: TreeNode | undefined): node is Node { + if (!!node && 'resolved' in node && 'item' in node) { + // tslint:disable-next-line:no-any + const { resolved, item } = (node as any); + return typeof resolved === 'boolean' && !!item; + } + return false; + } + + export function create(item: TypeHierarchyItem, direction: TypeHierarchyDirection, resolved: boolean = true): Node { + const items = TypeHierarchyDirection.Children === direction ? item.children : item.parents; + const node = { + id: v4(), + name: item.name, + description: item.detail, + parent: undefined, + location: Location.create(item.uri, item.selectionRange), + resolved, + children: items ? items.map(child => create(child, direction, false)) : [], + expanded: false, + visible: true, + selected: false, + kind: item.kind, + decorationData: decorationData(item, direction), + item + }; + // Trick: if the node is `resolved` and have zero `children`, make the node non-expandable. + if (resolved && node.children.length === 0) { + delete node.expanded; + } + return node; + } + + function decorationData(item: TypeHierarchyItem, direction: TypeHierarchyDirection): TreeDecoration.Data { + const captionSuffixes: TreeDecoration.CaptionAffix[] = [{ + data: new URI(item.uri).displayName, + fontData: { + color: 'var(--theia-ui-font-color2)', + } + }]; + if (item.detail) { + captionSuffixes.unshift({ + data: item.detail, + fontData: { + color: 'var(--theia-accent-color0)', + style: 'italic' + } + }); + } + const data = `${TypeHierarchyDirection.Children === direction ? '▼' : '▲'}`; + const color = `var(${TypeHierarchyDirection.Children === direction ? '--theia-error-color2' : '--theia-success-color2'})`; + return { + captionSuffixes, + captionPrefixes: [{ + data, + fontData: { + color, + style: 'bold' + } + }] + }; + } + + } + +} diff --git a/packages/typehierarchy/src/browser/typehierarchy-contribution.ts b/packages/typehierarchy/src/browser/typehierarchy-contribution.ts new file mode 100644 index 0000000000000..8038606b19715 --- /dev/null +++ b/packages/typehierarchy/src/browser/typehierarchy-contribution.ts @@ -0,0 +1,150 @@ +/******************************************************************************** + * 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, inject, named } from 'inversify'; +import { MenuModelRegistry } from '@theia/core/lib/common/menu'; +import { ApplicationShell } from '@theia/core/lib/browser/shell'; +import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding'; +import { Command, CommandRegistry } from '@theia/core/lib/common/command'; +import { EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser/editor-menu'; +import { EditorAccess } from '@theia/editor/lib/browser/editor-manager'; +import { AbstractViewContribution, OpenViewArguments } from '@theia/core/lib/browser/shell/view-contribution'; +import { TypeHierarchyTree } from './tree/typehierarchy-tree'; +import { TypeHierarchyTreeWidget } from './tree/typehierarchy-tree-widget'; +import { TypeHierarchyDirection } from '@theia/languages/lib/browser/typehierarchy/typehierarchy-protocol'; + +@injectable() +export class TypeHierarchyContribution extends AbstractViewContribution { + + @inject(EditorAccess) + @named(EditorAccess.CURRENT) + protected readonly editorAccess: EditorAccess; + + @inject(ApplicationShell) + protected readonly shell: ApplicationShell; + + constructor() { + super({ + widgetId: TypeHierarchyTreeWidget.WIDGET_ID, + widgetName: TypeHierarchyTreeWidget.WIDGET_LABEL, + defaultWidgetOptions: { + area: 'bottom' + }, + toggleCommandId: TypeHierarchyCommands.TOGGLE_VIEW.id, + toggleKeybinding: 'ctrlcmd+alt+a' + }); + } + + async openView(args?: Partial): Promise { + const widget = await super.openView(args); + const { selection, languageId } = this.editorAccess; + const direction = this.getDirection(args); + await widget.initialize({ location: selection, languageId, direction }); + return widget; + } + + registerCommands(commands: CommandRegistry): void { + super.registerCommands(commands); + commands.registerCommand(TypeHierarchyCommands.OPEN_SUBTYPE, { + execute: () => this.openViewOrFlipHierarchyDirection(TypeHierarchyDirection.Children), + isEnabled: this.isEnabled.bind(this) + }); + commands.registerCommand(TypeHierarchyCommands.OPEN_SUPERTYPE, { + execute: () => this.openViewOrFlipHierarchyDirection(TypeHierarchyDirection.Parents), + isEnabled: this.isEnabled.bind(this) + }); + } + + registerMenus(menus: MenuModelRegistry): void { + super.registerMenus(menus); + const menuPath = [...EDITOR_CONTEXT_MENU, 'type-hierarchy']; + menus.registerMenuAction(menuPath, { + commandId: TypeHierarchyCommands.OPEN_SUBTYPE.id + }); + menus.registerMenuAction(menuPath, { + commandId: TypeHierarchyCommands.OPEN_SUPERTYPE.id + }); + } + + registerKeybindings(keybindings: KeybindingRegistry): void { + super.registerKeybindings(keybindings); + keybindings.registerKeybinding({ + command: TypeHierarchyCommands.OPEN_SUBTYPE.id, + keybinding: 'ctrlcmd+alt+h' + }); + } + + /** + * Flips the hierarchy direction in the `Type Hierarchy` view, if it is active and has a valid root. + * Otherwise, calculates the type hierarchy based on the selection of the current editor. + */ + protected async openViewOrFlipHierarchyDirection(direction: TypeHierarchyDirection): Promise { + if (this.isEnabled()) { + const { activeWidget } = this.shell; + if (activeWidget instanceof TypeHierarchyTreeWidget && TypeHierarchyTree.RootNode.is(activeWidget.model.root)) { + await activeWidget.model.flipDirection(); + } else { + await this.openView({ + toggle: false, + activate: true, + direction + }); + } + } + } + + /** + * Enabled if the `current` editor has the `languageId` or the `Type Hierarchy` widget is the active one. + */ + protected isEnabled(languageId: string | undefined = this.editorAccess.languageId): boolean { + return !!languageId || this.shell.activeWidget instanceof TypeHierarchyTreeWidget; + } + + /** + * Extracts the type hierarchy direction from the argument. If the direction cannot be extracted, returns with the `Children` as the default type. + */ + protected getDirection(args?: Partial): TypeHierarchyDirection { + return !!args && !!args.direction ? args.direction : TypeHierarchyDirection.Children; + } + +} + +export interface TypeHierarchyOpenViewArguments extends OpenViewArguments { + + /** + * The type hierarchy direction for the view argument. + */ + readonly direction: TypeHierarchyDirection; + +} + +export namespace TypeHierarchyCommands { + + export const TOGGLE_VIEW: Command = { + id: 'typehierarchy:toggle' + }; + + export const OPEN_SUBTYPE: Command = { + id: 'typehierarchy:open-subtype', + label: 'Subtype Hierarchy' + }; + + export const OPEN_SUPERTYPE: Command = { + id: 'typehierarchy:open-supertype', + label: 'Supertype Hierarchy' + }; + +} diff --git a/packages/typehierarchy/src/browser/typehierarchy-frontend-module.ts b/packages/typehierarchy/src/browser/typehierarchy-frontend-module.ts new file mode 100644 index 0000000000000..3627ff51aac72 --- /dev/null +++ b/packages/typehierarchy/src/browser/typehierarchy-frontend-module.ts @@ -0,0 +1,33 @@ +/******************************************************************************** + * 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 { ContainerModule } from 'inversify'; +import { WidgetFactory } from '@theia/core/lib/browser/widget-manager'; +import { bindViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; +import { TypeHierarchyServiceProvider } from './typehierarchy-service'; +import { TypeHierarchyContribution } from './typehierarchy-contribution'; +import { TypeHierarchyTreeWidget } from './tree/typehierarchy-tree-widget'; +import { createHierarchyTreeWidget } from './tree/typehierarchy-tree-container'; + +import '../../src/browser/style/index.css'; + +export default new ContainerModule(bind => { + bind(TypeHierarchyServiceProvider).toSelf().inSingletonScope(); + bindViewContribution(bind, TypeHierarchyContribution); + bind(WidgetFactory).toDynamicValue(context => ({ + id: TypeHierarchyTreeWidget.WIDGET_ID, + createWidget: () => createHierarchyTreeWidget(context.container) + })); +}); diff --git a/packages/typehierarchy/src/browser/typehierarchy-service.ts b/packages/typehierarchy/src/browser/typehierarchy-service.ts new file mode 100644 index 0000000000000..a0938bc16cb8a --- /dev/null +++ b/packages/typehierarchy/src/browser/typehierarchy-service.ts @@ -0,0 +1,71 @@ +/******************************************************************************** + * 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, inject } from 'inversify'; +import { ILanguageClient } from '@theia/languages/lib/browser'; +import { LanguageClientProvider } from '@theia/languages/lib/browser/language-client-provider'; +import { + TypeHierarchyItem, + TypeHierarchyParams, + TypeHierarchyRequest, + ResolveTypeHierarchyRequest, + TypeHierarchyServerCapabilities, + ResolveTypeHierarchyItemParams +} from '@theia/languages/lib/browser/typehierarchy/typehierarchy-protocol'; + +@injectable() +export class TypeHierarchyServiceProvider { + + @inject(LanguageClientProvider) + protected readonly clientProvider: LanguageClientProvider; + + async get(languageId: string | undefined): Promise { + if (languageId) { + const client = await this.clientProvider.getLanguageClient(languageId); + if (client && client.initializeResult) { + const { typeHierarchyProvider } = client.initializeResult.capabilities as TypeHierarchyServerCapabilities; + if (!!typeHierarchyProvider) { + return new TypeHierarchyService(client, languageId); + } + } + } + return undefined; + } + +} + +export class TypeHierarchyService { + + constructor(protected readonly client: ILanguageClient, readonly languageId: string) { + } + + /** + * Performs the `textDocument/typeHierarchy` LS method invocations. + */ + async get(params: TypeHierarchyParams): Promise { + const item = await this.client.sendRequest(TypeHierarchyRequest.type, params); + return item ? item : undefined; + } + + /** + * Performs the `typeHierarchy/resolve` LS method call. + */ + async resolve(params: ResolveTypeHierarchyItemParams): Promise { + const item = await this.client.sendRequest(ResolveTypeHierarchyRequest.type, params); + return item ? item : undefined; + } + +} diff --git a/packages/typehierarchy/src/package.spec.ts b/packages/typehierarchy/src/package.spec.ts new file mode 100644 index 0000000000000..3f7a69d7263b4 --- /dev/null +++ b/packages/typehierarchy/src/package.spec.ts @@ -0,0 +1,29 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('typehierarchy package', () => { + + it('support code coverage statistics', () => true); + +}); diff --git a/tsconfig.json b/tsconfig.json index c7899938a03a7..9fd662f6241d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -94,6 +94,9 @@ "@theia/callhierarchy/lib/*": [ "packages/callhierarchy/src/*" ], + "@theia/typehierarchy/lib/*": [ + "packages/typehierarchy/src/*" + ], "@theia/variable-resolver/lib/*": [ "packages/variable-resolver/src/*" ],