-
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 7 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 |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| import { INodeData, NodeKind } from "./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 { | ||
|
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. addPackage constructor compressTree should be better organized to clearance to detect tree structures based on a list of 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. Second this. Currently, the compressTree will not work for this case. |
||
| 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 { | ||
|
Flanker32 marked this conversation as resolved.
Outdated
|
||
| // 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 { | ||
|
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. Typically, this is an adapter that we can apply here. |
||
| name: this.name, | ||
| moduleName: nodeData.moduleName, | ||
| path: nodeData.path, | ||
| uri: null, | ||
| kind: NodeKind.PackageRoot, | ||
| children: null, | ||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,5 +28,9 @@ export class Settings { | |
| return this._depdendencyConfig.get("syncWithFolderExplorer"); | ||
| } | ||
|
|
||
| public static isHierarchicalView(): boolean { | ||
| return this._depdendencyConfig.get("packagePresentation") === "hierarchical"; | ||
|
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. should avoid to use the configuration directly since the config may change at runtime, in your impl, change of this options will take effective on next update. see https://github.com/Microsoft/vscode-java-debug/blob/bcd69b52e4d941323cae662a154afd2a7852f43d/src/debugCodeLensProvider.ts#L38 |
||
| } | ||
|
|
||
| private static _depdendencyConfig: WorkspaceConfiguration = workspace.getConfiguration("java.dependency"); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,13 @@ | |
| // Licensed under the MIT license. | ||
|
|
||
| import { ExtensionContext, ProviderResult, TextEditor, TreeView, TreeViewVisibilityChangeEvent, Uri, window } from "vscode"; | ||
| import { Jdtls } from "../java/jdtls"; | ||
| import { INodeData } from "../java/nodeData"; | ||
| import { Settings } from "../settings"; | ||
| import { Utility } from "../utility"; | ||
| import { DataNode } from "./dataNode"; | ||
| import { DependencyDataProvider } from "./dependencyDataProvider"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
| import { PathProcesser } from "./pathProcesser"; | ||
|
|
||
| export class DependencyExplorer { | ||
|
|
||
|
|
@@ -40,16 +40,13 @@ export class DependencyExplorer { | |
| } | ||
|
|
||
| public reveal(uri: Uri): void { | ||
| Jdtls.resolvePath(uri.toString()).then((paths: INodeData[]) => { | ||
| this.revealPath(this._dataProvider, paths); | ||
| }); | ||
| PathProcesser.resolvePath(uri).then((paths: INodeData[]) => this.revealPath(this._dataProvider, paths)); | ||
| } | ||
|
|
||
| private revealPath(current: { getChildren: (element?: ExplorerNode) => ProviderResult<ExplorerNode[]> }, paths: INodeData[]) { | ||
| 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[]) => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| import { INodeData, NodeKind } from "../java/nodeData"; | ||
| import { PackageTreeNode } from "../java/packageTreeNode"; | ||
| 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 { PackageRootNode } from "./packageRootNode"; | ||
| import { ProjectNode } from "./projectNode"; | ||
| import { TypeRootNode } from "./typeRootNode"; | ||
|
|
||
| export class HierachicalPackageRootNode extends PackageRootNode { | ||
|
|
||
| public static async convertPaths(paths: INodeData[]): Promise<INodeData[]> { | ||
| const index = paths.findIndex((nodeData) => nodeData.kind === NodeKind.PackageRoot); | ||
| const projectNodeData = paths.find((nodeData) => nodeData.kind === NodeKind.Project); | ||
|
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. All these operations are model oriented. You can compute the tree without creating a view. For example, you can make the packagetree as a the model and build the hierarchy model outof the the view. |
||
| const packageRootNodeData = paths[index]; | ||
| const packageNodeData = paths[index + 1]; | ||
| const packageRootNode = new HierachicalPackageRootNode(packageRootNodeData, null, new ProjectNode(projectNodeData, null)); | ||
|
|
||
| const correspondDataNodes: INodeData[] = []; | ||
| let correspondNode = await packageRootNode.revealPath(packageNodeData); | ||
| while (correspondNode instanceof HierachicalPackageRootSubNode) { | ||
| correspondDataNodes.push({ | ||
| name: correspondNode.nodeData.name, | ||
| moduleName: null, | ||
| path: correspondNode.nodeData.path, | ||
| uri: null, | ||
| kind: NodeKind.PackageRoot, | ||
| children: null, | ||
| }); | ||
| correspondNode = correspondNode.getParent(); | ||
| } | ||
| const result = paths.slice(null, index + 1).concat(correspondDataNodes.reverse(), paths.slice(index + 2)); | ||
| return result; | ||
| } | ||
|
|
||
| constructor(nodeData: INodeData, parent: DataNode, _project: ProjectNode) { | ||
| super(nodeData, parent, _project); | ||
| } | ||
|
|
||
| public async revealPath(packageNodeData: INodeData): Promise<ExplorerNode> { | ||
| await this.getChildren(); | ||
| let packageTreeNode: PackageTreeNode = this.getPackageTree(); | ||
| let result: DataNode = null; | ||
| while (packageTreeNode && packageTreeNode.fullName !== packageNodeData.name) { | ||
| packageTreeNode = packageTreeNode.childs.find((child) => packageNodeData.name.startsWith(child.fullName)); | ||
| result = packageTreeNode ? new HierachicalPackageRootSubNode(packageTreeNode.getNodeDataFromPackageTreeNode(this.nodeData), | ||
| result, this._project, packageTreeNode) : null; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| protected createChildNodeList(): ExplorerNode[] { | ||
| const result = []; | ||
| if (this.nodeData.children && this.nodeData.children.length) { | ||
| this.sort(); | ||
| 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)); | ||
| } | ||
| }); | ||
| } | ||
| const packageNodeList = this.getHierarchicalPackageNodes(); | ||
|
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. don't introduce local var here |
||
| return packageNodeList.concat(result); | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| 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,58 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| import { Jdtls } from "../java/jdtls"; | ||
| import { INodeData, NodeKind } from "../java/nodeData"; | ||
| import { PackageTreeNode } from "../java/packageTreeNode"; | ||
| import { DataNode } from "./dataNode"; | ||
| import { ExplorerNode } from "./explorerNode"; | ||
| import { FileNode } from "./fileNode"; | ||
| import { FolderNode } from "./folderNode"; | ||
| 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.sort(); | ||
| 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)); | ||
| } | ||
| }); | ||
| } | ||
| const packageNodeList = this.getHierarchicalPackageNodes(); | ||
| return packageNodeList.concat(result); | ||
| } | ||
|
|
||
| protected getHierarchicalPackageNodes(): ExplorerNode[] { | ||
| const result = []; | ||
| this.packageTree.childs.forEach((childNode) => { | ||
|
Flanker32 marked this conversation as resolved.
Outdated
|
||
| result.push(new HierachicalPackageRootSubNode(childNode.getNodeDataFromPackageTreeNode(this.nodeData), this, this._project, childNode)); | ||
| }); | ||
| return result; | ||
| } | ||
| } | ||
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