Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@
"type": "boolean",
"description": "Synchronize dependency viewer selection with folder explorer",
"default": true
},
"java.dependency.packagePresentation": {
"type": "string",
"enum":["flat","hierarchical"],
"description": "Package presentation mode: flat or hierarchical",
"default": "flat"
}
}
},
Expand Down
65 changes: 65 additions & 0 deletions src/java/packageTreeNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { INodeData, NodeKind } from "./nodeData";

export class PackageTreeNode {

public static createEmptyRootNode(): PackageTreeNode {
return new PackageTreeNode("", "");
}

public name: string;
public fullName: string;
public childs: PackageTreeNode[] = [];
public isPackage: boolean = false;

private constructor(name: string, parentFullName: string) {
this.name = name;
this.fullName = parentFullName === "" ? name : parentFullName + "." + name;
}

public addPackage(packageName: string): void {
Copy link
Contributor

Choose a reason for hiding this comment

The 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:
a.b
a.b.c.d
a.b.c.d.e

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second this. Currently, the compressTree will not work for this case.

const packages = packageName.split(".");
this.addSubPackage(packages);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.addSubPackage(packageName.split("."));

}

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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,
};
}

private addSubPackage(packages: string[]): void {
if (!packages.length) {
this.isPackage = true;
return;
}
const subPackageName = packages.shift();
const childNode = this.childs.find((child) => child.name === subPackageName);
if (childNode) {
childNode.addSubPackage(packages);
} else {
const newNode = new PackageTreeNode(subPackageName, this.fullName);
newNode.addSubPackage(packages);
this.childs.push(newNode);
}
}
}
7 changes: 6 additions & 1 deletion src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export class Settings {
return;
}
const updatedConfig = workspace.getConfiguration("java.dependency");
if (updatedConfig.showOutline !== this._depdendencyConfig.showOutline) {
if (updatedConfig.showOutline !== this._depdendencyConfig.showOutline
|| updatedConfig.packagePresentation !== this._depdendencyConfig.packagePresentation) {
commands.executeCommand(Commands.VIEW_PACKAGE_REFRESH);
}
this._depdendencyConfig = updatedConfig;
Expand All @@ -28,5 +29,9 @@ export class Settings {
return this._depdendencyConfig.get("syncWithFolderExplorer");
}

public static isHierarchicalView(): boolean {
return this._depdendencyConfig.get("packagePresentation") === "hierarchical";
Copy link
Contributor

Choose a reason for hiding this comment

The 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");
}
4 changes: 2 additions & 2 deletions src/views/containerNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Jdtls } from "../java/jdtls";
import { INodeData, NodeKind } from "../java/nodeData";
import { DataNode } from "./dataNode";
import { ExplorerNode } from "./explorerNode";
import { PackageRootNode } from "./packageRootNode";
import { NodeFactory } from "./nodeFactory";
import { ProjectNode } from "./projectNode";

export class ContainerNode extends DataNode {
Expand All @@ -21,7 +21,7 @@ export class ContainerNode extends DataNode {
if (this.nodeData.children && this.nodeData.children.length) {
this.sort();
this.nodeData.children.forEach((classpathNode) => {
result.push(new PackageRootNode(classpathNode, this, this._project));
result.push(NodeFactory.createPackageRootNode(classpathNode, this, this._project));
});
}
return result;
Expand Down
5 changes: 5 additions & 0 deletions src/views/dataNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export abstract class DataNode extends ExplorerNode {
return this._nodeData.path;
}

public async getCorrespondChildNodeWithNodeData(nodeData: INodeData): Promise<DataNode> {
const childs: ExplorerNode[] = await this.getChildren();
return <DataNode>childs.find((child: DataNode) => child.nodeData.name === nodeData.name && child.path === nodeData.path);
}

public getChildren(): ProviderResult<ExplorerNode[]> {
if (!this._nodeData.children) {
return this.loadData().then((res) => {
Expand Down
32 changes: 32 additions & 0 deletions src/views/dependencyDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Commands } from "../commands";
import { Jdtls } from "../java/jdtls";
import { INodeData, NodeKind } from "../java/nodeData";
import { Telemetry } from "../telemetry";
import { DataNode } from "./dataNode";
import { ExplorerNode } from "./explorerNode";
import { ProjectNode } from "./projectNode";
import { WorkspaceNode } from "./workspaceNode";
Expand Down Expand Up @@ -67,6 +68,37 @@ export class DependencyDataProvider implements TreeDataProvider<ExplorerNode> {
return element.getParent();
}

public async getRootNodeByData(nodeData: INodeData): Promise<DataNode> {
// Server only return project nodes, so use get root projects function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said, in order to get the data, creating the view is not an acceptable solution here. DON't mix the model with your view. You either either directly get the data from the server, or patch the Workspsace data to the path side.

const rootNodes: ExplorerNode[] = await this.getRootProjects();
return <DataNode>rootNodes.find((node: DataNode) => node.path === nodeData.path && node.nodeData.name === nodeData.name);
}

private async getRootProjects(): Promise<ExplorerNode[]> {
let result = new Array<ExplorerNode>();
const folders = workspace.workspaceFolders;
if (folders && folders.length) {
if (folders.length > 1) {
const workspaces = folders.map((folder) => new WorkspaceNode({
name: folder.name,
uri: folder.uri.toString(),
kind: NodeKind.Workspace,
}, null));
// return projects of all workspaces
for (const singleworkspace of workspaces) {
const projects = await singleworkspace.getChildren();
result = result.concat(projects);
}
} else {
const projectsNodeData = await Jdtls.getProjects(folders[0].uri.toString());
projectsNodeData.forEach((project) => {
result.push(new ProjectNode(project, null));
});
}
}
return result;
}

private getRootNodes(): Thenable<ExplorerNode[]> {
return new Promise((resolve, reject) => {
this._rootItems = new Array<ExplorerNode>();
Expand Down
48 changes: 10 additions & 38 deletions src/views/dependencyExplorer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { ExtensionContext, ProviderResult, TextEditor, TreeView, TreeViewVisibilityChangeEvent, Uri, window } from "vscode";
import { ExtensionContext, 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";
Expand Down Expand Up @@ -39,44 +38,17 @@ export class DependencyExplorer {
public dispose(): void {
}

public reveal(uri: Uri): void {
Jdtls.resolvePath(uri.toString()).then((paths: INodeData[]) => {
this.revealPath(this._dataProvider, paths);
});
}

private revealPath(current: { getChildren: (element?: ExplorerNode) => ProviderResult<ExplorerNode[]> }, paths: INodeData[]) {
if (!current) {
return;
public async reveal(uri: Uri): Promise<void> {
const paths: INodeData[] = await Jdtls.resolvePath(uri.toString());
// Get correspond project node from dataProvider
let node = await this._dataProvider.getRootNodeByData(paths.shift());
while (paths.length && node) {
node = await node.getCorrespondChildNodeWithNodeData(paths.shift());
}

Copy link
Contributor

Choose a reason for hiding this comment

The 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[]) => {
this.visitChildren(children, paths);
});
if (this._dependencyViewer.visible) {
this._dependencyViewer.reveal(node);
} else {
this.visitChildren(<DataNode[]>res, paths);
}
}

private visitChildren(children: DataNode[], paths: INodeData[]): void {
if (children && paths) {
for (const c of children) {
if (paths[0] && c.path === paths[0].path && c.nodeData.name === paths[0].name) {
if (paths.length === 1) {
if (this._dependencyViewer.visible) {
this._dependencyViewer.reveal(c);
} else {
this._selectionWhenHidden = c;
}
} else {
paths.shift();
this.revealPath(c, paths);
}
break;
}
}
this._selectionWhenHidden = node;
}
}
}
Loading