Skip to content

Commit 95bf714

Browse files
committed
Add hierarchical package view, for issue #57
1 parent c94fc2b commit 95bf714

File tree

4 files changed

+173
-6
lines changed

4 files changed

+173
-6
lines changed

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@
6262
"type": "boolean",
6363
"description": "Synchronize dependency viewer selection with folder explorer",
6464
"default": true
65+
},
66+
"java.dependency.isHierarchicalView": {
67+
"type": "boolean",
68+
"description": "Switch whether package shows in hierarchical view",
69+
"default": false
6570
}
6671
}
6772
},

src/settings.ts

+4
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ export class Settings {
2828
return this._depdendencyConfig.get("syncWithFolderExplorer");
2929
}
3030

31+
public static isHierarchicalView(): boolean {
32+
return this._depdendencyConfig.get("isHierarchicalView");
33+
}
34+
3135
private static _depdendencyConfig: WorkspaceConfiguration = workspace.getConfiguration("java.dependency");
3236
}

src/views/dependencyExplorer.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Utility } from "../utility";
99
import { DataNode } from "./dataNode";
1010
import { DependencyDataProvider } from "./dependencyDataProvider";
1111
import { ExplorerNode } from "./explorerNode";
12+
import { PackageRootNode } from "./packageRootNode";
1213

1314
export class DependencyExplorer {
1415

@@ -49,7 +50,6 @@ export class DependencyExplorer {
4950
if (!current) {
5051
return;
5152
}
52-
5353
const res = current.getChildren();
5454
if (Utility.isThenable(res)) {
5555
res.then((children: DataNode[]) => {
@@ -71,8 +71,23 @@ export class DependencyExplorer {
7171
this._selectionWhenHidden = c;
7272
}
7373
} else {
74-
paths.shift();
75-
this.revealPath(c, paths);
74+
// Resove Hierarchical packages
75+
const node = paths.shift();
76+
if (Settings.isHierarchicalView() && c instanceof PackageRootNode) {
77+
const packageNode = paths.shift();
78+
const res = c.getChildren();
79+
if (Utility.isThenable(res)) {
80+
res.then(() => {
81+
const correspondPackageNode = c.getPackageNodeFromNodeData(packageNode);
82+
this.revealPath(correspondPackageNode, paths);
83+
});
84+
} else {
85+
const correspondPackageNode = c.getPackageNodeFromNodeData(packageNode);
86+
this.revealPath(correspondPackageNode, paths);
87+
}
88+
} else {
89+
this.revealPath(c, paths);
90+
}
7691
}
7792
break;
7893
}

src/views/packageRootNode.ts

+146-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import { Jdtls } from "../java/jdtls";
55
import { INodeData, NodeKind } from "../java/nodeData";
66
import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData";
7+
import { Settings } from "../settings";
8+
import { Utility } from "../utility";
79
import { DataNode } from "./dataNode";
810
import { ExplorerNode } from "./explorerNode";
911
import { FileNode } from "./fileNode";
@@ -14,15 +16,45 @@ import { TypeRootNode } from "./typeRootNode";
1416

1517
export class PackageRootNode extends DataNode {
1618

17-
constructor(nodeData: INodeData, parent: DataNode, private _project: ProjectNode) {
19+
private packageTree: PackageTreeNode;
20+
21+
constructor(nodeData: INodeData, parent: DataNode, private _project: ProjectNode, packageTree: PackageTreeNode = null) {
1822
super(nodeData, parent);
23+
this.packageTree = packageTree;
24+
}
25+
26+
// Get correspond packageRootNode when revealPath
27+
public getPackageNodeFromNodeData(classPackage: INodeData): PackageRootNode {
28+
// tslint:disable-next-line:no-this-assignment
29+
let packageRootNode: PackageRootNode = this;
30+
while (packageRootNode.packageTree === null || packageRootNode.packageTree.fullName !== classPackage.name) {
31+
let noMatchPackage: boolean = true;
32+
packageRootNode.createChildNodeList().forEach((child) => {
33+
if (child instanceof PackageRootNode && classPackage.name.startsWith(child.packageTree.fullName)) {
34+
packageRootNode = child;
35+
noMatchPackage = false;
36+
}
37+
});
38+
if (noMatchPackage) {
39+
return null;
40+
}
41+
}
42+
return packageRootNode;
1943
}
2044

2145
protected loadData(): Thenable<INodeData[]> {
22-
return Jdtls.getPackageData({ kind: NodeKind.PackageRoot, projectUri: this._project.nodeData.uri, rootPath: this.nodeData.path });
46+
if (this.packageTree && this.packageTree.isPackage) {
47+
// load package data
48+
return Jdtls.getPackageData({
49+
kind: NodeKind.Package, projectUri: this._project.nodeData.uri, path: this.packageTree.fullName, rootPath: this.nodeData.path,
50+
});
51+
} else {
52+
return Jdtls.getPackageData({ kind: NodeKind.PackageRoot, projectUri: this._project.nodeData.uri, rootPath: this.nodeData.path });
53+
}
54+
2355
}
2456

25-
protected createChildNodeList(): ExplorerNode[] {
57+
protected createFlatChildNodeList(): ExplorerNode[] {
2658
const result = [];
2759
if (this.nodeData.children && this.nodeData.children.length) {
2860
this.sort();
@@ -41,7 +73,67 @@ export class PackageRootNode extends DataNode {
4173
return result;
4274
}
4375

76+
protected createHierarchicalChildNodeList(): ExplorerNode[] {
77+
const result = [];
78+
if (this.nodeData.children && this.nodeData.children.length) {
79+
this.nodeData.children.forEach((data) => {
80+
if (data.kind === NodeKind.File) {
81+
result.push(new FileNode(data, this));
82+
} else if (data.kind === NodeKind.Folder) {
83+
result.push(new FolderNode(data, this, this._project, this));
84+
} else if (data.kind === NodeKind.TypeRoot) {
85+
result.push(new TypeRootNode(data, this));
86+
}
87+
});
88+
}
89+
this.getHierarchicalPackageNodes().forEach((node) => result.push(node));
90+
result.sort();
91+
return result;
92+
}
93+
94+
protected createChildNodeList(): ExplorerNode[] {
95+
if (Settings.isHierarchicalView()) {
96+
return this.createHierarchicalChildNodeList();
97+
} else {
98+
return this.createFlatChildNodeList();
99+
}
100+
}
101+
102+
protected getHierarchicalPackageNodes(): ExplorerNode[] {
103+
const result = [];
104+
const packageTree = this.packageTree ? this.packageTree : this.getPackageTree();
105+
packageTree.childs.forEach((childNode) => {
106+
const childNodeData: INodeData = {
107+
name: childNode.name,
108+
moduleName: this.nodeData.moduleName,
109+
path: this.nodeData.path,
110+
uri: null,
111+
kind: NodeKind.PackageRoot,
112+
children: null,
113+
};
114+
result.push(new PackageRootNode(childNodeData, this, this._project, childNode));
115+
});
116+
return result;
117+
}
118+
119+
// Generage tree for packages, use for Hierarchical view
120+
protected getPackageTree(): PackageTreeNode {
121+
const result: PackageTreeNode = new PackageTreeNode("", "");
122+
if (this.nodeData.children && this.nodeData.children.length) {
123+
this.nodeData.children.forEach((child) => {
124+
if (child.kind === NodeKind.Package) {
125+
result.addPackage(child.name);
126+
}
127+
});
128+
}
129+
result.compressTree();
130+
return result;
131+
}
132+
44133
protected get iconPath(): { light: string; dark: string } {
134+
if (this.packageTree !== null) {
135+
return ExplorerNode.resolveIconPath("package");
136+
}
45137
const data = <IPackageRootNodeData>this.nodeData;
46138
if (data.entryKind === PackageRootKind.K_BINARY) {
47139
return ExplorerNode.resolveIconPath("jar");
@@ -50,3 +142,54 @@ export class PackageRootNode extends DataNode {
50142
}
51143
}
52144
}
145+
146+
class PackageTreeNode {
147+
public name: string;
148+
public fullName: string;
149+
public childs: PackageTreeNode[] = [];
150+
public isPackage: boolean = false;
151+
152+
constructor(packageName: string, parentName: string) {
153+
const splitPackageName = packageName.split(".");
154+
this.name = splitPackageName[0];
155+
this.fullName = parentName === "" ? this.name : parentName + "." + this.name;
156+
if (splitPackageName.length > 1) {
157+
this.childs.push(new PackageTreeNode(packageName.substring(this.name.length + 1), this.fullName));
158+
} else {
159+
this.isPackage = true;
160+
}
161+
}
162+
163+
public addPackage(packageName: string): void {
164+
const splitPackageName = packageName.split(".");
165+
const firstSubName = splitPackageName[0];
166+
const restname = packageName.substring(firstSubName.length + 1);
167+
168+
let contains: boolean = false;
169+
this.childs.forEach((child) => {
170+
if (child.name === firstSubName) {
171+
if (restname === "") {
172+
child.isPackage = true;
173+
} else {
174+
child.addPackage(restname);
175+
}
176+
contains = true;
177+
}
178+
});
179+
if (!contains) {
180+
this.childs.push(new PackageTreeNode(packageName, this.fullName));
181+
}
182+
}
183+
184+
public compressTree(): void {
185+
// Don't compress the root node
186+
while (this.name !== "" && this.childs.length === 1 && !this.isPackage) {
187+
const child = this.childs[0];
188+
this.fullName = this.fullName + "." + child.name;
189+
this.name = this.name + "." + child.name;
190+
this.childs = child.childs;
191+
this.isPackage = child.isPackage;
192+
}
193+
this.childs.forEach((child) => child.compressTree());
194+
}
195+
}

0 commit comments

Comments
 (0)