-
Notifications
You must be signed in to change notification settings - Fork 101
Add hierarchical package view, for issue #57 #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
95bf714
6c8fad4
11269cd
4cbf6f7
19e1c0b
8b29ec1
053c463
cc21dcd
bfd578b
4e75cad
fdc2cb7
44fb789
6055b4b
bc78c92
7854efc
3543836
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { Utility } from "../utility"; | |
| import { DataNode } from "./dataNode"; | ||
| import { DependencyDataProvider } from "./dependencyDataProvider"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
| import { HierarchicalNode } from "./hierarchicalNode"; | ||
|
|
||
| export class DependencyExplorer { | ||
|
|
||
|
|
@@ -49,7 +50,6 @@ export class DependencyExplorer { | |
| if (!current) { | ||
| return; | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid the empty diff if possible. You can always provide another PR for format/coding convention to avoid distraction. |
||
| const res = current.getChildren(); | ||
| if (Utility.isThenable(res)) { | ||
| res.then((children: DataNode[]) => { | ||
|
|
@@ -71,8 +71,14 @@ export class DependencyExplorer { | |
| this._selectionWhenHidden = c; | ||
| } | ||
| } else { | ||
| paths.shift(); | ||
| this.revealPath(c, paths); | ||
| // Resove Hierarchical packages | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: Resolve ==> Resolve |
||
| if (c instanceof HierarchicalNode && c.isHierarchicalView()) { | ||
| c.revealPath(paths) | ||
| .then((revealResult) => { this.revealPath(revealResult[0], revealResult[1]); }); | ||
| } else { | ||
| paths.shift(); | ||
| this.revealPath(c, paths); | ||
| } | ||
| } | ||
| break; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { Jdtls } from "../java/jdtls"; | ||
| import { INodeData, NodeKind } from "../java/nodeData"; | ||
| import { DataNode } from "./dataNode"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
| import { FileNode } from "./fileNode"; | ||
| import { FolderNode } from "./folderNode"; | ||
| import { PackageTreeNode } from "./packageTreeNode"; | ||
| import { ProjectNode } from "./projectNode"; | ||
| import { TypeRootNode } from "./typeRootNode"; | ||
|
|
||
| export class HierachicalPackageRootSubNode extends DataNode { | ||
|
|
||
| public packageTree: PackageTreeNode; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This class code is duplicate with the HierarchicalPackageRootNode, could we model these two types in one class? |
||
|
|
||
| constructor(nodeData: INodeData, parent: DataNode, private _project: ProjectNode, packageTree: PackageTreeNode = null) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. = null should be removed |
||
| super(nodeData, parent); | ||
| this.packageTree = packageTree; | ||
| } | ||
|
|
||
| protected loadData(): Thenable<any[]> { | ||
| return Jdtls.getPackageData({ | ||
| kind: NodeKind.Package, | ||
| projectUri: this._project.nodeData.uri, | ||
| path: this.packageTree.fullName, | ||
| rootPath: this.nodeData.path, | ||
| }); | ||
| } | ||
|
|
||
| protected get iconPath(): { light: string; dark: string } { | ||
| return ExplorerNode.resolveIconPath("package"); | ||
| } | ||
|
|
||
| protected createChildNodeList(): ExplorerNode[] { | ||
| const result = []; | ||
| if (this.nodeData.children && this.nodeData.children.length) { | ||
| this.nodeData.children.forEach((data) => { | ||
| if (data.kind === NodeKind.File) { | ||
| result.push(new FileNode(data, this)); | ||
| } else if (data.kind === NodeKind.Folder) { | ||
| result.push(new FolderNode(data, this, this._project, this)); | ||
| } else if (data.kind === NodeKind.TypeRoot) { | ||
| result.push(new TypeRootNode(data, this)); | ||
| } | ||
| }); | ||
| } | ||
| this.getHierarchicalPackageNodes().forEach((node) => result.push(node)); | ||
| result.sort(); | ||
| return result; | ||
| } | ||
|
|
||
| protected getHierarchicalPackageNodes(): ExplorerNode[] { | ||
| const result = []; | ||
| this.packageTree.childs.forEach((childNode) => { | ||
|
Flanker32 marked this conversation as resolved.
Outdated
|
||
| const childNodeData: INodeData = { | ||
| name: childNode.name, | ||
| moduleName: this.nodeData.moduleName, | ||
| path: this.nodeData.path, | ||
| uri: null, | ||
| kind: NodeKind.PackageRoot, | ||
| children: null, | ||
| }; | ||
| result.push(new HierachicalPackageRootSubNode(childNodeData, this, this._project, childNode)); | ||
| }); | ||
| return result; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { INodeData } from "../java/nodeData"; | ||
| import { DataNode } from "./dataNode"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
|
|
||
| export abstract class HierarchicalNode extends DataNode { | ||
| public abstract createFlatChildNodeList(): ExplorerNode[]; | ||
| public abstract createHierarchicalChildNodeList(): ExplorerNode[]; | ||
| public abstract isHierarchicalView(): boolean; | ||
| public abstract revealPath(paths: INodeData[]): Promise<[ExplorerNode, INodeData[]]>; | ||
|
|
||
| protected createChildNodeList(): ExplorerNode[] { | ||
| return this.isHierarchicalView() ? this.createHierarchicalChildNodeList() : this.createFlatChildNodeList(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,25 +4,29 @@ | |
| import { Jdtls } from "../java/jdtls"; | ||
| import { INodeData, NodeKind } from "../java/nodeData"; | ||
| import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData"; | ||
| import { Settings } from "../settings"; | ||
| import { DataNode } from "./dataNode"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
| import { FileNode } from "./fileNode"; | ||
| import { FolderNode } from "./folderNode"; | ||
| import { HierachicalPackageRootSubNode } from "./hierachicalPackageRootSubNode"; | ||
| import { HierarchicalNode } from "./hierarchicalNode"; | ||
| import { PackageNode } from "./packageNode"; | ||
| import { PackageTreeNode } from "./packageTreeNode"; | ||
| import { ProjectNode } from "./projectNode"; | ||
| import { TypeRootNode } from "./typeRootNode"; | ||
|
|
||
| export class PackageRootNode extends DataNode { | ||
| export class PackageRootNode extends HierarchicalNode { | ||
|
|
||
| constructor(nodeData: INodeData, parent: DataNode, private _project: ProjectNode) { | ||
| constructor(nodeData: INodeData, parent: DataNode, protected _project: ProjectNode) { | ||
| super(nodeData, parent); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should NOT mixed the flat package node and hierarchy package node in the same class. It violate SRP and will make the code hard to main in the future. You can create two class/types for different purpose.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made an abstract class named |
||
| } | ||
|
|
||
| protected loadData(): Thenable<INodeData[]> { | ||
| return Jdtls.getPackageData({ kind: NodeKind.PackageRoot, projectUri: this._project.nodeData.uri, rootPath: this.nodeData.path }); | ||
| public isHierarchicalView(): boolean { | ||
| return Settings.getPackagePresentation() === "hierarchical"; | ||
| } | ||
|
|
||
| protected createChildNodeList(): ExplorerNode[] { | ||
| public createFlatChildNodeList(): ExplorerNode[] { | ||
| const result = []; | ||
| if (this.nodeData.children && this.nodeData.children.length) { | ||
| this.sort(); | ||
|
|
@@ -40,6 +44,41 @@ export class PackageRootNode extends DataNode { | |
| } | ||
| return result; | ||
| } | ||
| public createHierarchicalChildNodeList(): ExplorerNode[] { | ||
| const result = []; | ||
| if (this.nodeData.children && this.nodeData.children.length) { | ||
| this.nodeData.children.forEach((data) => { | ||
| if (data.kind === NodeKind.File) { | ||
| result.push(new FileNode(data, this)); | ||
| } else if (data.kind === NodeKind.Folder) { | ||
| result.push(new FolderNode(data, this, this._project, this)); | ||
| } else if (data.kind === NodeKind.TypeRoot) { | ||
| result.push(new TypeRootNode(data, this)); | ||
| } | ||
| }); | ||
| } | ||
| this.getHierarchicalPackageNodes().forEach((node) => result.push(node)); | ||
| result.sort(); | ||
| return result; | ||
| } | ||
|
|
||
| public async revealPath(paths: INodeData[]): Promise<[ExplorerNode, INodeData[]]> { | ||
| await this.getChildren(); | ||
| const packageRootNodeData = paths.shift(); | ||
| const packageNodeData = paths.shift(); | ||
| let packageTreeNode: PackageTreeNode = this.getPackageTree(); | ||
| // tslint:disable-next-line:no-this-assignment | ||
| let result: DataNode = this; | ||
| while (packageTreeNode.childs.length && packageTreeNode.fullName !== packageNodeData.name) { | ||
| packageTreeNode.childs.forEach((child) => { | ||
| if (packageNodeData.name.startsWith(child.fullName)) { | ||
| result = new HierachicalPackageRootSubNode(child.getNodeDataFromPackageTreeNode(this.nodeData), result, this._project, child); | ||
| packageTreeNode = child; | ||
| } | ||
| }); | ||
| } | ||
| return [result, paths]; | ||
| } | ||
|
|
||
| protected get iconPath(): { light: string; dark: string } { | ||
| const data = <IPackageRootNodeData>this.nodeData; | ||
|
|
@@ -49,4 +88,30 @@ export class PackageRootNode extends DataNode { | |
| return ExplorerNode.resolveIconPath("packagefolder"); | ||
| } | ||
| } | ||
|
|
||
| protected getHierarchicalPackageNodes(): ExplorerNode[] { | ||
| const result = []; | ||
| const packageTree = this.getPackageTree(); | ||
| packageTree.childs.forEach((childNode) => { | ||
| result.push(new HierachicalPackageRootSubNode(childNode.getNodeDataFromPackageTreeNode(this.nodeData), this, this._project, childNode)); | ||
| }); | ||
| return result; | ||
| } | ||
|
|
||
| protected loadData(): Thenable<INodeData[]> { | ||
| return Jdtls.getPackageData({ kind: NodeKind.PackageRoot, projectUri: this._project.nodeData.uri, rootPath: this.nodeData.path }); | ||
| } | ||
|
|
||
| private getPackageTree(): PackageTreeNode { | ||
| const result: PackageTreeNode = new PackageTreeNode("", ""); | ||
| if (this.nodeData.children && this.nodeData.children.length) { | ||
| this.nodeData.children.forEach((child) => { | ||
| if (child.kind === NodeKind.Package) { | ||
| result.addPackage(child.name); | ||
| } | ||
| }); | ||
| } | ||
| result.compressTree(); | ||
| return result; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import { INodeData, NodeKind } from "../java/nodeData"; | ||
|
|
||
| export class PackageTreeNode { | ||
| public name: string; | ||
| public fullName: string; | ||
| public childs: PackageTreeNode[] = []; | ||
| public isPackage: boolean = false; | ||
|
|
||
| constructor(packageName: string, parentName: string) { | ||
| const splitPackageName = packageName.split("."); | ||
| this.name = splitPackageName[0]; | ||
| this.fullName = parentName === "" ? this.name : parentName + "." + this.name; | ||
| if (splitPackageName.length > 1) { | ||
| this.childs.push(new PackageTreeNode(packageName.substring(this.name.length + 1), this.fullName)); | ||
| } else { | ||
| this.isPackage = true; | ||
| } | ||
| } | ||
|
|
||
| public addPackage(packageName: string): void { | ||
| const splitPackageName = packageName.split("."); | ||
| const firstSubName = splitPackageName[0]; | ||
| const restname = packageName.substring(firstSubName.length + 1); | ||
|
|
||
| let contains: boolean = false; | ||
| this.childs.forEach((child) => { | ||
| if (child.name === firstSubName) { | ||
| if (restname === "") { | ||
| child.isPackage = true; | ||
| } else { | ||
| child.addPackage(restname); | ||
| } | ||
| contains = true; | ||
| } | ||
| }); | ||
| if (!contains) { | ||
| this.childs.push(new PackageTreeNode(packageName, this.fullName)); | ||
| } | ||
| } | ||
|
|
||
| public compressTree(): void { | ||
| // Don't compress the root node | ||
| while (this.name !== "" && this.childs.length === 1 && !this.isPackage) { | ||
| const child = this.childs[0]; | ||
| this.fullName = this.fullName + "." + child.name; | ||
| this.name = this.name + "." + child.name; | ||
| this.childs = child.childs; | ||
| this.isPackage = child.isPackage; | ||
| } | ||
| this.childs.forEach((child) => child.compressTree()); | ||
| } | ||
|
|
||
| public getNodeDataFromPackageTreeNode(nodeData: INodeData): INodeData { | ||
| return { | ||
| name: this.name, | ||
| moduleName: nodeData.moduleName, | ||
| path: nodeData.path, | ||
| uri: null, | ||
| kind: NodeKind.PackageRoot, | ||
| children: null, | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Package presentation mode: flat or hierarchical