Skip to content

Commit 58050b0

Browse files
authored
Add hierarchical package view, for issue #57 (#117)
1 parent 4c63db6 commit 58050b0

15 files changed

+331
-56
lines changed

package-lock.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@
4747
"dark": "images/dark/icon-refresh.svg",
4848
"light": "images/light/icon-refresh.svg"
4949
}
50+
},
51+
{
52+
"command": "java.view.package.changeRepresentation",
53+
"title": "Change package representation",
54+
"category": "Java",
55+
"icon": {
56+
"dark": "images/dark/package.svg",
57+
"light": "images/light/package.svg"
58+
}
5059
}
5160
],
5261
"configuration": {
@@ -62,6 +71,15 @@
6271
"type": "boolean",
6372
"description": "Synchronize dependency viewer selection with folder explorer",
6473
"default": true
74+
},
75+
"java.dependency.packagePresentation": {
76+
"type": "string",
77+
"enum": [
78+
"flat",
79+
"hierarchical"
80+
],
81+
"description": "Package presentation mode: flat or hierarchical",
82+
"default": "flat"
6583
}
6684
}
6785
},
@@ -70,6 +88,11 @@
7088
{
7189
"command": "java.view.package.refresh",
7290
"when": "view == javaDependencyExplorer",
91+
"group": "navigation@1"
92+
},
93+
{
94+
"command": "java.view.package.changeRepresentation",
95+
"when": "view == javaDependencyExplorer",
7396
"group": "navigation@0"
7497
}
7598
]

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export namespace Commands {
1010
*/
1111
export const EXECUTE_WORKSPACE_COMMAND = "java.execute.workspaceCommand";
1212

13+
export const VIEW_PACKAGE_CHANGEREPRESENTATION = "java.view.package.changeRepresentation";
14+
1315
export const VIEW_PACKAGE_REFRESH = "java.view.package.refresh";
1416

1517
export const VIEW_PACKAGE_OPEN_FILE = "java.view.package.openFile";
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
import { INodeData, NodeKind } from "./nodeData";
5+
6+
export class HierachicalPackageNodeData implements INodeData {
7+
8+
public static createHierachicalNodeDataByPackageList(packageList: INodeData[]): HierachicalPackageNodeData {
9+
const result = new HierachicalPackageNodeData("", "");
10+
packageList.forEach((nodeData) => result.addSubPackage(nodeData.name.split("."), nodeData));
11+
result.compressTree();
12+
return result;
13+
}
14+
15+
public name: string;
16+
public children = [];
17+
public displayName: string;
18+
private nodeData: INodeData = null;
19+
20+
public get uri() {
21+
return this.nodeData.uri;
22+
}
23+
24+
public get moduleName() {
25+
return this.nodeData.moduleName;
26+
}
27+
28+
public get path() {
29+
return this.nodeData.path;
30+
}
31+
32+
public get kind() {
33+
return this.nodeData ? this.nodeData.kind : NodeKind.Package;
34+
}
35+
36+
public get isPackage() {
37+
return this.nodeData !== null;
38+
}
39+
40+
private constructor(displayName: string, parentName: string) {
41+
this.displayName = displayName;
42+
this.name = parentName === "" ? displayName : parentName + "." + displayName;
43+
}
44+
45+
private compressTree(): void {
46+
// Don't compress the root node
47+
while (this.name !== "" && this.children.length === 1 && !this.isPackage) {
48+
const child = this.children[0];
49+
this.name = this.name + "." + child.displayName;
50+
this.displayName = this.displayName + "." + child.displayName;
51+
this.children = child.children;
52+
this.nodeData = child.nodeData;
53+
}
54+
this.children.forEach((child) => child.compressTree());
55+
}
56+
57+
private addSubPackage(packages: string[], nodeData: INodeData): void {
58+
if (!packages.length) {
59+
this.nodeData = nodeData;
60+
return;
61+
}
62+
const subPackageDisplayName = packages.shift();
63+
const childNode = this.children.find((child) => child.displayName === subPackageDisplayName);
64+
if (childNode) {
65+
childNode.addSubPackage(packages);
66+
} else {
67+
const newNode = new HierachicalPackageNodeData(subPackageDisplayName, this.name);
68+
newNode.addSubPackage(packages, nodeData);
69+
this.children.push(newNode);
70+
}
71+
}
72+
}

src/settings.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
import { commands, ConfigurationChangeEvent, ExtensionContext, workspace, WorkspaceConfiguration } from "vscode";
5+
import { instrumentOperation } from "vscode-extension-telemetry-wrapper";
56
import { Commands } from "./commands";
67

78
export class Settings {
@@ -12,12 +13,21 @@ export class Settings {
1213
return;
1314
}
1415
const updatedConfig = workspace.getConfiguration("java.dependency");
15-
if (updatedConfig.showOutline !== this._depdendencyConfig.showOutline) {
16+
if (updatedConfig.showOutline !== this._depdendencyConfig.showOutline
17+
|| updatedConfig.packagePresentation !== this._depdendencyConfig.packagePresentation) {
1618
commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH);
1719
}
1820
this._depdendencyConfig = updatedConfig;
1921

2022
}));
23+
24+
const instrumented = instrumentOperation(Commands.VIEW_PACKAGE_CHANGEREPRESENTATION, Settings.changePackageRepresentation);
25+
context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_CHANGEREPRESENTATION, instrumented));
26+
}
27+
28+
public static changePackageRepresentation(): void {
29+
const representationSetting = Settings.isHierarchicalView() ? PackagePresentation.Flat : PackagePresentation.Hierarchical;
30+
workspace.getConfiguration().update("java.dependency.packagePresentation", representationSetting, false);
2131
}
2232

2333
public static showOutline(): boolean {
@@ -28,5 +38,14 @@ export class Settings {
2838
return this._depdendencyConfig.get("syncWithFolderExplorer");
2939
}
3040

41+
public static isHierarchicalView(): boolean {
42+
return this._depdendencyConfig.get("packagePresentation") === PackagePresentation.Hierarchical;
43+
}
44+
3145
private static _depdendencyConfig: WorkspaceConfiguration = workspace.getConfiguration("java.dependency");
3246
}
47+
48+
enum PackagePresentation {
49+
Flat = "flat",
50+
Hierarchical = "hierarchical",
51+
}

src/views/containerNode.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Jdtls } from "../java/jdtls";
55
import { INodeData, NodeKind } from "../java/nodeData";
66
import { DataNode } from "./dataNode";
77
import { ExplorerNode } from "./explorerNode";
8-
import { PackageRootNode } from "./packageRootNode";
8+
import { NodeFactory } from "./nodeFactory";
99
import { ProjectNode } from "./projectNode";
1010

1111
export class ContainerNode extends DataNode {
@@ -21,7 +21,7 @@ export class ContainerNode extends DataNode {
2121
if (this.nodeData.children && this.nodeData.children.length) {
2222
this.sort();
2323
this.nodeData.children.forEach((classpathNode) => {
24-
result.push(new PackageRootNode(classpathNode, this, this._project));
24+
result.push(NodeFactory.createPackageRootNode(classpathNode, this, this._project));
2525
});
2626
}
2727
return result;

src/views/dataNode.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Telemetry } from "../telemetry";
77
import { ExplorerNode } from "./explorerNode";
88

99
export abstract class DataNode extends ExplorerNode {
10-
constructor(private _nodeData: INodeData, parent: DataNode) {
10+
constructor(protected _nodeData: INodeData, parent: DataNode) {
1111
super(parent);
1212
}
1313

@@ -32,6 +32,13 @@ export abstract class DataNode extends ExplorerNode {
3232
return this._nodeData.path;
3333
}
3434

35+
public async revealPaths(paths: INodeData[]): Promise<DataNode> {
36+
const childNodeData = paths.shift();
37+
const childs: ExplorerNode[] = await this.getChildren();
38+
const childNode = <DataNode>childs.find((child: DataNode) => child.nodeData.name === childNodeData.name && child.path === childNodeData.path);
39+
return childNode === null ? null : (paths.length ? childNode.revealPaths(paths) : childNode);
40+
}
41+
3542
public getChildren(): ProviderResult<ExplorerNode[]> {
3643
if (!this._nodeData.children) {
3744
return this.loadData().then((res) => {

src/views/dependencyDataProvider.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Commands } from "../commands";
1010
import { Jdtls } from "../java/jdtls";
1111
import { INodeData, NodeKind } from "../java/nodeData";
1212
import { Telemetry } from "../telemetry";
13+
import { DataNode } from "./dataNode";
1314
import { ExplorerNode } from "./explorerNode";
1415
import { ProjectNode } from "./projectNode";
1516
import { WorkspaceNode } from "./workspaceNode";
@@ -67,6 +68,28 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
6768
return element.getParent();
6869
}
6970

71+
public async revealPaths(paths: INodeData[]): Promise<DataNode> {
72+
const projectNodeData = paths.shift();
73+
const projects = await this.getRootProjects();
74+
const correspondProject = <DataNode>projects.find((node: DataNode) =>
75+
node.path === projectNodeData.path && node.nodeData.name === projectNodeData.name);
76+
return correspondProject.revealPaths(paths);
77+
}
78+
79+
private async getRootProjects(): Promise<ExplorerNode[]> {
80+
const rootElements = this._rootItems ? this._rootItems : await this.getChildren();
81+
if (rootElements[0] instanceof ProjectNode) {
82+
return rootElements;
83+
} else {
84+
let result = [];
85+
for (const singleworkspace of rootElements) {
86+
const projects = await singleworkspace.getChildren();
87+
result = result.concat(projects);
88+
}
89+
return result;
90+
}
91+
}
92+
7093
private getRootNodes(): Thenable<ExplorerNode[]> {
7194
return new Promise((resolve, reject) => {
7295
this._rootItems = new Array<ExplorerNode>();

0 commit comments

Comments
 (0)