Skip to content

Commit dad7afa

Browse files
committed
Add support to view inherited members #2342
The reference view API is used to show the document outline with inherited symbols using the new java.ls extension method. Signed-off-by: Gayan Perera <[email protected]>
1 parent 03d6bb4 commit dad7afa

File tree

8 files changed

+184
-3
lines changed

8 files changed

+184
-3
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,11 @@
10311031
"command": "java.action.changeBaseType",
10321032
"title": "%java.action.changeBaseType%",
10331033
"category": "Java"
1034+
},
1035+
{
1036+
"command": "java.action.showExtendedOutline",
1037+
"title": "%java.action.showExtendedOutline%",
1038+
"category": "Java"
10341039
}
10351040
],
10361041
"keybindings": [

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@
2121
"java.action.showClassHierarchy": "Show Class Hierarchy",
2222
"java.action.showSupertypeHierarchy": "Show Supertype Hierarchy",
2323
"java.action.showSubtypeHierarchy": "Show Subtype Hierarchy",
24-
"java.action.changeBaseType": "Base on this Type"
24+
"java.action.changeBaseType": "Base on this Type",
25+
"java.action.showExtendedOutline": "Open Extended Outline"
2526
}

src/commands.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,10 @@ export namespace Commands {
277277
export const UPGRADE_GRADLE_WRAPPER = '_java.gradle.upgradeWrapper';
278278

279279
export const LOMBOK_CONFIGURE = "java.lombokConfigure";
280+
281+
/**
282+
* Show Extended Outline for current document.
283+
*/
284+
export const SHOW_EXTEND_OUTLINE = 'java.action.showExtendedOutline';
285+
280286
}

src/outline/extendedOutlineTree.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { LanguageClient } from "vscode-languageclient/node";
2+
import { SymbolTree } from "../typeHierarchy/references-view";
3+
import * as vscode from "vscode";
4+
import { getActiveLanguageClient } from "../extension";
5+
import { ExtendedOutlineTreeInput } from "./model";
6+
7+
export class ExtendedOutlineTree {
8+
private api: SymbolTree;
9+
private client: LanguageClient;
10+
public initialized: boolean;
11+
12+
constructor() {
13+
this.initialized = false;
14+
}
15+
16+
async initialize() {
17+
// It uses a new publisher id in June 2022 Update, check both old/new id for compatibility
18+
// See https://github.com/microsoft/vscode/pull/152213
19+
const referencesViewExt = vscode.extensions.getExtension<SymbolTree>('vscode.references-view')
20+
?? vscode.extensions.getExtension<SymbolTree>('ms-vscode.references-view');
21+
this.api = await referencesViewExt?.activate();
22+
this.client = await getActiveLanguageClient();
23+
this.initialized = true;
24+
}
25+
26+
async open(uri: vscode.Uri) {
27+
if (!this.initialized) {
28+
await this.initialize();
29+
}
30+
31+
if (!this.api) {
32+
return;
33+
}
34+
const input: ExtendedOutlineTreeInput = new ExtendedOutlineTreeInput(new vscode.Location(uri, new vscode.Position(0, 0)));
35+
this.api.setInput(input);
36+
}
37+
}
38+
39+
export const extendedOutlineTree: ExtendedOutlineTree = new ExtendedOutlineTree();
40+

src/outline/model.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Event, Location, Position, ProviderResult, Range, TextDocumentShowOptions, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri } from "vscode";
2+
import { DocumentSymbolParams, LanguageClient, TextDocumentIdentifier } from "vscode-languageclient/node";
3+
import { getActiveLanguageClient } from "../extension";
4+
import { getThemeIcon } from "../themeUtils";
5+
import { SymbolItemNavigation, SymbolTreeInput, SymbolTreeModel } from "../typeHierarchy/references-view";
6+
import { ExtendedDocumentSymbol, ExtendedDocumentSymbolsRequest } from "./protocol";
7+
8+
export class ExtendedOutlineTreeInput implements SymbolTreeInput<ExtendedDocumentSymbol> {
9+
readonly contextValue: string = "javaExtendedOutline";
10+
readonly title: string = "Extended Outline";
11+
client: LanguageClient;
12+
13+
constructor(readonly location: Location) {
14+
}
15+
16+
async resolve(): Promise<SymbolTreeModel<ExtendedDocumentSymbol>> {
17+
if (!this.client) {
18+
this.client = await getActiveLanguageClient();
19+
}
20+
21+
const params: DocumentSymbolParams = {
22+
textDocument: TextDocumentIdentifier.create(this.location.uri.toString())
23+
};
24+
const symbols = await this.client.sendRequest(ExtendedDocumentSymbolsRequest.type, params);
25+
const treeModel: SymbolTreeModel<ExtendedDocumentSymbol> = {
26+
provider: new ExtendedOutlineProvider(symbols, this.client),
27+
message: undefined,
28+
navigation: new ExtendedOutlineModel()
29+
};
30+
return Promise.resolve(treeModel);
31+
}
32+
33+
with(location: Location): SymbolTreeInput<ExtendedDocumentSymbol> {
34+
return new ExtendedOutlineTreeInput(location);
35+
}
36+
}
37+
38+
export class ExtendedOutlineModel implements SymbolItemNavigation<ExtendedDocumentSymbol> {
39+
nearest(_uri: Uri, _position: Position): ExtendedDocumentSymbol | undefined {
40+
return undefined;
41+
}
42+
43+
next(from: ExtendedDocumentSymbol): ExtendedDocumentSymbol {
44+
return from;
45+
}
46+
previous(from: ExtendedDocumentSymbol): ExtendedDocumentSymbol {
47+
return from;
48+
}
49+
location(item: ExtendedDocumentSymbol): Location {
50+
return new Location(Uri.parse(item.uri), new Range(new Position(item.range.start.line, item.range.start.character),
51+
new Position(item.range.end.line, item.range.end.character)));
52+
}
53+
54+
}
55+
56+
class ExtendedOutlineProvider implements TreeDataProvider<ExtendedDocumentSymbol> {
57+
onDidChangeTreeData?: Event<void | ExtendedDocumentSymbol>;
58+
59+
constructor(readonly symbols: ExtendedDocumentSymbol[], readonly client: LanguageClient) { }
60+
61+
getTreeItem(element: ExtendedDocumentSymbol): TreeItem | Thenable<TreeItem> {
62+
let state = TreeItemCollapsibleState.None;
63+
if (element.children != null && element.children.length > 0)
64+
state = TreeItemCollapsibleState.Collapsed;
65+
66+
const item: TreeItem = new TreeItem(element.name, state);
67+
item.description = element.detail;
68+
item.iconPath = getThemeIcon(element.kind - 1);
69+
item.command = (element.uri) ? {
70+
command: 'vscode.open',
71+
title: 'Open Symbol Definition Location',
72+
arguments: [
73+
element.uri, <TextDocumentShowOptions>{ selection: element.selectionRange }
74+
]
75+
} : undefined;
76+
return item;
77+
}
78+
79+
getChildren(element?: ExtendedDocumentSymbol): ProviderResult<ExtendedDocumentSymbol[]> {
80+
if (element == null) {
81+
return Promise.resolve(this.symbols);
82+
} else {
83+
if (element.children != null) {
84+
return Promise.resolve(element.children as ExtendedDocumentSymbol[]);
85+
}
86+
}
87+
return undefined;
88+
}
89+
90+
getParent?(_element: ExtendedDocumentSymbol): ProviderResult<ExtendedDocumentSymbol> {
91+
return undefined;
92+
}
93+
}

src/outline/protocol.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { DocumentSymbol, DocumentSymbolParams, RequestType } from "vscode-languageclient";
2+
3+
export namespace ExtendedDocumentSymbolsRequest {
4+
export const type = new RequestType<DocumentSymbolParams, ExtendedDocumentSymbol[], void>('java/extendedDocumentSymbols');
5+
}
6+
7+
export interface ExtendedDocumentSymbol extends DocumentSymbol {
8+
uri: string;
9+
}

src/standardLanguageClient.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { JavaInlayHintsProvider } from "./inlayHintsProvider";
3737
import { gradleCodeActionMetadata, GradleCodeActionProvider } from "./gradle/gradleCodeActionProvider";
3838
import { checkLombokDependency } from "./lombokSupport";
3939
import { askForProjects, projectConfigurationUpdate, upgradeGradle } from "./standardLanguageClientUtils";
40+
import { extendedOutlineTree } from "./outline/extendedOutlineTree";
4041

4142
const extensionName = 'Language Support for Java';
4243
const GRADLE_CHECKSUM = "gradle/checksum/prompt";
@@ -178,7 +179,7 @@ export class StandardLanguageClient {
178179
const options: string[] = [];
179180
const info = notification.data as GradleCompatibilityInfo;
180181
const highestJavaVersion = Number(info.highestJavaVersion);
181-
let runtimes = await findRuntimes({checkJavac: true, withVersion: true, withTags: true});
182+
let runtimes = await findRuntimes({ checkJavac: true, withVersion: true, withTags: true });
182183
runtimes = runtimes.filter(runtime => {
183184
return runtime.version.major <= highestJavaVersion;
184185
});
@@ -533,6 +534,17 @@ export class StandardLanguageClient {
533534
}
534535
}));
535536

537+
context.subscriptions.push(commands.registerCommand(Commands.SHOW_EXTEND_OUTLINE, (location: any) => {
538+
if (location instanceof Uri) {
539+
extendedOutlineTree.open(location);
540+
} else {
541+
if (window.activeTextEditor?.document?.languageId !== "java") {
542+
return;
543+
}
544+
extendedOutlineTree.open(window.activeTextEditor.document.uri);
545+
}
546+
}));
547+
536548
buildPath.registerCommands(context);
537549
sourceAction.registerCommands(this.languageClient, context);
538550
refactorAction.registerCommands(this.languageClient, context);
@@ -661,7 +673,7 @@ function setProjectConfigurationUpdate(languageClient: LanguageClient, uri: Uri,
661673
}
662674

663675
function decodeBase64(text: string): string {
664-
return Buffer.from(text, 'base64').toString('ascii');
676+
return Buffer.from(text, 'base64').toString('ascii');
665677
}
666678

667679
export function showNoLocationFound(message: string): void {

src/themeUtils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { SymbolKind, ThemeIcon } from "vscode";
2+
3+
const themeIconIds = [
4+
'symbol-file', 'symbol-module', 'symbol-namespace', 'symbol-package', 'symbol-class', 'symbol-method',
5+
'symbol-property', 'symbol-field', 'symbol-constructor', 'symbol-enum', 'symbol-interface',
6+
'symbol-function', 'symbol-variable', 'symbol-constant', 'symbol-string', 'symbol-number', 'symbol-boolean',
7+
'symbol-array', 'symbol-object', 'symbol-key', 'symbol-null', 'symbol-enum-member', 'symbol-struct',
8+
'symbol-event', 'symbol-operator', 'symbol-type-parameter'
9+
];
10+
11+
export function getThemeIcon(kind: SymbolKind): ThemeIcon | undefined {
12+
const id = themeIconIds[kind];
13+
return id ? new ThemeIcon(id) : undefined;
14+
}
15+

0 commit comments

Comments
 (0)