diff --git a/package.json b/package.json index 46e537fb94..f842b21c10 100644 --- a/package.json +++ b/package.json @@ -948,15 +948,8 @@ "category": "Java" }, { - "command": "java.action.showSupertypeHierarchy", - "title": "%java.action.showSupertypeHierarchy%", - "icon": "$(type-hierarchy-super)", - "category": "Java" - }, - { - "command": "java.action.showSubtypeHierarchy", - "title": "%java.action.showSubtypeHierarchy%", - "icon": "$(type-hierarchy-sub)", + "command": "java.action.changeBaseType", + "title": "%java.action.changeBaseType%", "category": "Java" } ], @@ -1043,11 +1036,7 @@ "when": "javaLSReady && editorIsOpen" }, { - "command": "java.action.showSubtypeHierarchy", - "when": "false" - }, - { - "command": "java.action.showSupertypeHierarchy", + "command": "java.action.changeBaseType", "when": "false" }, { @@ -1071,21 +1060,11 @@ "when": "java:serverMode == LightWeight" } ], - "view/title": [ - { - "command": "java.action.showClassHierarchy", - "group": "navigation@-1", - "when": "view == references-view.tree && reference-list.hasResult && reference-list.source == typeHierarchy && typeHierarchySymbolKind != 10" - }, - { - "command": "java.action.showSupertypeHierarchy", - "group": "navigation@0", - "when": "view == references-view.tree && reference-list.hasResult && reference-list.source == javaTypeHierarchy" - }, + "view/item/context": [ { - "command": "java.action.showSubtypeHierarchy", - "group": "navigation@1", - "when": "view == references-view.tree && reference-list.hasResult && reference-list.source == javaTypeHierarchy" + "command": "java.action.changeBaseType", + "group": "1", + "when": "view == references-view.tree && reference-list.hasResult && reference-list.source == javaTypeHierarchy && viewItem != 'false'" } ] } diff --git a/package.nls.json b/package.nls.json index 4d466d33db..b9c2b17446 100644 --- a/package.nls.json +++ b/package.nls.json @@ -15,7 +15,5 @@ "java.show.server.task.status": "Show Build Job Status", "java.action.navigateToSuperImplementation": "Go to Super Implementation", "java.action.showClassHierarchy": "Show Class Hierarchy", - "java.action.showSupertypeHierarchy": "Show Supertype Hierarchy", - "java.action.showSubtypeHierarchy": "Show Subtype Hierarchy", "java.action.changeBaseType": "Base on this Type" } \ No newline at end of file diff --git a/package.nls.zh.json b/package.nls.zh.json index 190d83ef3a..f8d8e3f003 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -15,7 +15,5 @@ "java.show.server.task.status": "显示工作状态", "java.action.navigateToSuperImplementation": "转到父类实现", "java.action.showClassHierarchy": "显示类的继承关系", - "java.action.showSupertypeHierarchy": "显示父类层次结构", - "java.action.showSubtypeHierarchy": "显示子类层次结构", "java.action.changeBaseType": "基于此类型" } \ No newline at end of file diff --git a/src/commands.ts b/src/commands.ts index b4b34540dd..dbec3d2bf1 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -192,18 +192,14 @@ export namespace Commands { * Navigate To Super Method Command. */ export const NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation'; - /** - * Show SuperType Hierarchy of given Selection. - */ - export const SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy'; - /** - * Show SubType Hierarchy of given Selection. - */ - export const SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy'; /** * Show Class Hierarchy of given Selection. */ export const SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy'; + /** + * Change the base type of Type Hierarchy. + */ + export const CHANGE_BASE_TYPE = 'java.action.changeBaseType'; /** * Show server task status */ diff --git a/src/standardLanguageClient.ts b/src/standardLanguageClient.ts index 556c72969e..dbd157eb37 100644 --- a/src/standardLanguageClient.ts +++ b/src/standardLanguageClient.ts @@ -1,6 +1,6 @@ 'use strict'; -import { ExtensionContext, window, workspace, commands, Uri, ProgressLocation, ViewColumn, EventEmitter, extensions, Location, languages, CodeActionKind, TextEditor, CancellationToken, ConfigurationTarget, Range, Position, TypeHierarchyItem } from "vscode"; +import { ExtensionContext, window, workspace, commands, Uri, ProgressLocation, ViewColumn, EventEmitter, extensions, Location, languages, CodeActionKind, TextEditor, CancellationToken, ConfigurationTarget, Range, Position } from "vscode"; import { Commands } from "./commands"; import { serverStatus, ServerStatusKind } from "./serverStatus"; import { prepareExecutable, awaitServerConnection } from "./javaServerStarter"; @@ -33,7 +33,7 @@ import { buildFilePatterns } from './plugin'; import { pomCodeActionMetadata, PomCodeActionProvider } from "./pom/pomCodeActionProvider"; import { findRuntimes, IJavaRuntime } from "jdk-utils"; import { TypeHierarchyFeature } from "vscode-languageclient/lib/common/proposed.typeHierarchy"; -import { TypeHierarchyDirection, TypeItem, TypesModel } from "./typeHierarchy/model.reference-view"; +import { CodeTypeHierarchyItem } from "./typeHierarchy/protocol"; const extensionName = 'Language Support for Java'; const GRADLE_CHECKSUM = "gradle/checksum/prompt"; @@ -383,16 +383,8 @@ export class StandardLanguageClient { } })); - context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUPERTYPE_HIERARCHY, () => { - const typesModel: TypesModel = new TypesModel(TypeHierarchyDirection.Supertypes, [typeHierarchyTree.currentItem]); - const typeItem: TypeItem = new TypeItem(typesModel, typeHierarchyTree.currentItem, undefined); - commands.executeCommand("references-view.showSupertypes", typeItem); - })); - - context.subscriptions.push(commands.registerCommand(Commands.SHOW_SUBTYPE_HIERARCHY, () => { - const typesModel: TypesModel = new TypesModel(TypeHierarchyDirection.Subtypes, [typeHierarchyTree.currentItem]); - const typeItem: TypeItem = new TypeItem(typesModel, typeHierarchyTree.currentItem, undefined); - commands.executeCommand("references-view.showSubtypes", typeItem); + context.subscriptions.push(commands.registerCommand(Commands.CHANGE_BASE_TYPE, async (item: CodeTypeHierarchyItem) => { + typeHierarchyTree.changeBaseItem(item); })); context.subscriptions.push(commands.registerCommand(Commands.COMPILE_WORKSPACE, (isFullCompile: boolean, token?: CancellationToken) => { diff --git a/src/typeHierarchy/model.reference-view.ts b/src/typeHierarchy/model.reference-view.ts deleted file mode 100644 index e93ed89597..0000000000 --- a/src/typeHierarchy/model.reference-view.ts +++ /dev/null @@ -1,214 +0,0 @@ -import * as vscode from 'vscode'; -import { SymbolItemDragAndDrop, SymbolItemEditorHighlights, SymbolItemNavigation, SymbolTreeInput } from './references-view'; - -export class TypesTreeInput implements SymbolTreeInput { - - readonly title: string; - readonly contextValue: string = 'typeHierarchy'; - - constructor( - readonly location: vscode.Location, - readonly direction: TypeHierarchyDirection, - ) { - this.title = direction === TypeHierarchyDirection.Supertypes - ? 'Supertypes Of' - : 'Subtypes Of'; - } - - async resolve() { - - const items = await Promise.resolve(vscode.commands.executeCommand('vscode.prepareTypeHierarchy', this.location.uri, this.location.range.start)); - const model = new TypesModel(this.direction, items ?? []); - const provider = new TypeItemDataProvider(model); - - if (model.roots.length === 0) { - return; - } - - return { - provider, - get message() { return model.roots.length === 0 ? 'No results.' : undefined; }, - navigation: model, - dispose() { - provider.dispose(); - } - }; - } - - with(location: vscode.Location): TypesTreeInput { - return new TypesTreeInput(location, this.direction); - } -} - -export const enum TypeHierarchyDirection { - Subtypes = 'subtypes', - Supertypes = 'supertypes' -} - -export class TypeItem { - - children?: TypeItem[]; - - constructor( - readonly model: TypesModel, - readonly item: vscode.TypeHierarchyItem, - readonly parent: TypeItem | undefined, - ) { } - - remove(): void { - this.model.remove(this); - } -} - -export class TypesModel implements SymbolItemNavigation, SymbolItemEditorHighlights, SymbolItemDragAndDrop { - - readonly roots: TypeItem[] = []; - - private readonly _onDidChange = new vscode.EventEmitter(); - readonly onDidChange = this._onDidChange.event; - - constructor(readonly direction: TypeHierarchyDirection, items: vscode.TypeHierarchyItem[]) { - this.roots = items.map(item => new TypeItem(this, item, undefined)); - } - - private async _resolveTypes(currentType: TypeItem): Promise { - if (this.direction === TypeHierarchyDirection.Supertypes) { - const types = await vscode.commands.executeCommand('vscode.provideSupertypes', currentType.item); - return types ? types.map(item => new TypeItem(this, item, currentType)) : []; - } else { - const types = await vscode.commands.executeCommand('vscode.provideSubtypes', currentType.item); - return types ? types.map(item => new TypeItem(this, item, currentType)) : []; - } - } - - async getTypeChildren(item: TypeItem): Promise { - if (!item.children) { - item.children = await this._resolveTypes(item); - } - return item.children; - } - - // -- dnd - - getDragUri(item: TypeItem): vscode.Uri | undefined { - return asResourceUrl(item.item.uri, item.item.range); - } - - // -- navigation - - location(currentType: TypeItem) { - return new vscode.Location(currentType.item.uri, currentType.item.range); - } - - nearest(uri: vscode.Uri, _position: vscode.Position): TypeItem | undefined { - return this.roots.find(item => item.item.uri.toString() === uri.toString()) ?? this.roots[0]; - } - - next(from: TypeItem): TypeItem { - return this._move(from, true) ?? from; - } - - previous(from: TypeItem): TypeItem { - return this._move(from, false) ?? from; - } - - private _move(item: TypeItem, fwd: boolean) { - if (item.children?.length) { - return fwd ? item.children[0] : tail(item.children); - } - const array = this.roots.includes(item) ? this.roots : item.parent?.children; - if (array?.length) { - const idx = array.indexOf(item); - const delta = fwd ? 1 : -1; - return array[idx + delta + array.length % array.length]; - } - } - - // --- highlights - - getEditorHighlights(currentType: TypeItem, uri: vscode.Uri): vscode.Range[] | undefined { - return currentType.item.uri.toString() === uri.toString() ? [currentType.item.selectionRange] : undefined; - } - - remove(item: TypeItem) { - const isInRoot = this.roots.includes(item); - const siblings = isInRoot ? this.roots : item.parent?.children; - if (siblings) { - del(siblings, item); - this._onDidChange.fire(this); - } - } -} - -class TypeItemDataProvider implements vscode.TreeDataProvider { - - private readonly _emitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData = this._emitter.event; - - private readonly _modelListener: vscode.Disposable; - - constructor(private _model: TypesModel) { - this._modelListener = _model.onDidChange(e => this._emitter.fire(e instanceof TypeItem ? e : undefined)); - } - - dispose(): void { - this._emitter.dispose(); - this._modelListener.dispose(); - } - - getTreeItem(element: TypeItem): vscode.TreeItem { - - const item = new vscode.TreeItem(element.item.name); - item.description = element.item.detail; - item.contextValue = 'type-item'; - item.iconPath = TypeItemDataProvider.getThemeIcon(element.item.kind); - item.command = { - command: 'vscode.open', - title: 'Open Type', - arguments: [ - element.item.uri, - { selection: element.item.selectionRange.with({ end: element.item.selectionRange.start }) } - ] - }; - item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; - return item; - } - - getChildren(element?: TypeItem | undefined) { - return element - ? this._model.getTypeChildren(element) - : this._model.roots; - } - - getParent(element: TypeItem) { - return element.parent; - } - - private static themeIconIds = [ - 'symbol-file', 'symbol-module', 'symbol-namespace', 'symbol-package', 'symbol-class', 'symbol-method', - 'symbol-property', 'symbol-field', 'symbol-constructor', 'symbol-enum', 'symbol-interface', - 'symbol-function', 'symbol-variable', 'symbol-constant', 'symbol-string', 'symbol-number', 'symbol-boolean', - 'symbol-array', 'symbol-object', 'symbol-key', 'symbol-null', 'symbol-enum-member', 'symbol-struct', - 'symbol-event', 'symbol-operator', 'symbol-type-parameter' - ]; - - private static getThemeIcon(kind: vscode.SymbolKind): vscode.ThemeIcon | undefined { - const id = TypeItemDataProvider.themeIconIds[kind]; - return id ? new vscode.ThemeIcon(id) : undefined; - } -} - -function del(array: T[], e: T): void { - const idx = array.indexOf(e); - if (idx >= 0) { - array.splice(idx, 1); - } -} - -function tail(array: T[]): T | undefined { - return array[array.length - 1]; -} - -function asResourceUrl(uri: vscode.Uri, range: vscode.Range): vscode.Uri { - return uri.with({ fragment: `L${1 + range.start.line},${1 + range.start.character}-${1 + range.end.line},${1 + range.end.character}` }); -} \ No newline at end of file diff --git a/src/typeHierarchy/model.ts b/src/typeHierarchy/model.ts index f1bcca6531..b498eea0a8 100644 --- a/src/typeHierarchy/model.ts +++ b/src/typeHierarchy/model.ts @@ -92,7 +92,7 @@ class TypeHierarchyTreeDataProvider implements vscode.TreeDataProvider { + this.setTypeHierarchy(new vscode.Location(item.uri, item.range.start)); + } + + private async isValidRequestPosition(uri: vscode.Uri, position: vscode.Position) { + const doc = await vscode.workspace.openTextDocument(uri); + let range = doc.getWordRangeAtPosition(position); + if (!range) { + range = doc.getWordRangeAtPosition(position, /[^\s]+/); + } + return Boolean(range); + } } export const typeHierarchyTree: TypeHierarchyTree = new TypeHierarchyTree(); diff --git a/src/typeHierarchy/util.ts b/src/typeHierarchy/util.ts index 81e5f46505..f163701aba 100644 --- a/src/typeHierarchy/util.ts +++ b/src/typeHierarchy/util.ts @@ -14,10 +14,8 @@ export async function getRootItem(client: LanguageClient, typeHierarchyItem: Cod if (!typeHierarchyItem) { return undefined; } - const lspItem = client.code2ProtocolConverter.asTypeHierarchyItem(typeHierarchyItem); - lspItem.data = typeHierarchyItem.data; const supertypeItems = await client.sendRequest(Proposed.TypeHierarchySupertypesRequest.type, { - item: lspItem + item: client.code2ProtocolConverter.asTypeHierarchyItem(typeHierarchyItem) }, token); if (supertypeItems.length === 0) { return typeHierarchyItem; diff --git a/test/standard-mode-suite/extension.test.ts b/test/standard-mode-suite/extension.test.ts index 00efba535f..77de5ff05c 100644 --- a/test/standard-mode-suite/extension.test.ts +++ b/test/standard-mode-suite/extension.test.ts @@ -66,9 +66,8 @@ suite('Java Language Extension - Standard', () => { Commands.SWITCH_SERVER_MODE, Commands.UPDATE_SOURCE_ATTACHMENT_CMD, Commands.RUNTIME_VALIDATION_OPEN, + Commands.CHANGE_BASE_TYPE, Commands.SHOW_CLASS_HIERARCHY, - Commands.SHOW_SUBTYPE_HIERARCHY, - Commands.SHOW_SUPERTYPE_HIERARCHY, ].sort(); const foundJavaCommands = commands.filter((value) => { return JAVA_COMMANDS.indexOf(value)>=0 || value.startsWith('java.');