Skip to content
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

add dependency treeview #647

Merged
merged 9 commits into from
Jul 6, 2021

Conversation

ShihanMeng618
Copy link
Contributor

@ShihanMeng618 ShihanMeng618 commented Jun 28, 2021

To implement dependency tree requested in #161,
and the dependencies can be expanded and collapsed.

image

it also includes information about duplicates and conflicts:

image

@ShihanMeng618 ShihanMeng618 marked this pull request as draft June 28, 2021 03:15
@ShihanMeng618 ShihanMeng618 marked this pull request as ready for review June 28, 2021 03:15
@Eskibear
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@Eskibear Eskibear left a comment

Choose a reason for hiding this comment

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

Overall it's a working, but the implemetation can be refined.

What I think of the workflow is:

  1. run some maven commands in background to fetch a raw data of dependencies in plaintext format.
  2. parse plaintext, you get a dependency tree.
  3. for each tree node, implement ITreeItem to tell vscode how to create a corresponding TreeItem for each dependency.

BTW, CI is failing, please also fix it.

src/utils/mavenUtils.ts Outdated Show resolved Hide resolved
src/utils/mavenUtils.ts Outdated Show resolved Hide resolved
@@ -83,7 +91,7 @@ async function executeInBackground(mvnArgs: string, pomfile?: string): Promise<a
if (code === 0) {
resolve(code);
} else {
reject(new Error(`Background process terminated with code ${code}.`));
reject(new Error(`Background process terminated with code ${code} and the failed command can be found in output channel.`));
Copy link
Member

Choose a reason for hiding this comment

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

extra information/guidance should be provided outside.

Copy link
Member

Choose a reason for hiding this comment

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

Let's leave it to another PR, which improves overall UX of error handling

const DUPLICATE_VALUE: string = "omitted for duplicate";
const CONFLICT_VALUE: string = "omitted for conflict";

export class Dependencies implements ITreeItem {
Copy link
Member

Choose a reason for hiding this comment

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

Use Dependency or MavenDependency instead of Dependencies

Copy link
Member

Choose a reason for hiding this comment

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

What I imagine:

  • basic properties of a Dependency should at least include groupId, artifactId, version, scope, which are parsed from plaintext from your implementation.
  • in getTreeItem, you contruct an TreeItem using above props.

treeContent = treeContent.replace(/\|/g, " ");
treeContent = treeContent.replace(/\\/g, "+");
treeContent = treeContent.replace(/\n/g, "\r\n");
return Promise.resolve(this.getDepsInString(treeContent));
Copy link
Member

Choose a reason for hiding this comment

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

getDepsInString => parseDependencies

public dependency: string;
public pomPath: string;
private curDep: string;
private separator: string;
Copy link
Member

Choose a reason for hiding this comment

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

what's it for?

src/explorer/model/Dependencies.ts Outdated Show resolved Hide resolved
src/explorer/model/Dependencies.ts Outdated Show resolved Hide resolved
src/explorer/model/Dependencies.ts Outdated Show resolved Hide resolved
}

public getTreeItem(): vscode.TreeItem | Thenable<vscode.TreeItem> {
this.curDep = this.dependency.split(this.separator, 1)[0];
Copy link
Member

Choose a reason for hiding this comment

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

  1. parsing work should be extracted into another utility.
  2. during parsing with some complicated regular expression, it's better to add comments about i) a sample input and ii) the matched parts.

@Eskibear
Copy link
Member

npm run tslint locally before you commit

const outputDirectory: string = path.dirname(pomPath);
const outputFileName: string = "dependency-graph";
await executeInBackground(`depgraph:graph -DgraphFormat=text -DshowDuplicates -DshowConflicts -DshowVersions -DshowGroupIds -DoutputDirectory="${outputDirectory}" -DoutputFileName="${outputFileName}"`, pomPath);
return await readFileIfExists(`${outputDirectory}\\${outputFileName}.txt`);
Copy link
Member

Choose a reason for hiding this comment

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

\\ might not work with Unix-like systems

Copy link
Member

Choose a reason for hiding this comment

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

use path.join

Copy link
Member

@Eskibear Eskibear left a comment

Choose a reason for hiding this comment

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

Dependency itself can be tree-like, I think we don't have to create an extra class TreeNode.
Does below structure work?

Dependency {
parent: Dependency | undefined
addChild(value: string | Dependency)
// other props
....
}

}
}

export function parseDependency(parentNode: TreeNode): Dependency[] {
Copy link
Member

Choose a reason for hiding this comment

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

Don't export it. If you want to reuse this function, place it into utilities.

this.value = value;
}

public addChildValue(value: string): void {
Copy link
Member

Choose a reason for hiding this comment

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

addChild(value: string) is simpler and carries enough information.

this.children.push(child);
}

public addChildNode(node: TreeNode): void {
Copy link
Member

Choose a reason for hiding this comment

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

addChild(node: TreeNode) is enough.
See https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads for overloads

// Licensed under the MIT license.

import * as vscode from "vscode";
import { parseDependency } from "./DependenciesMenu";
Copy link
Member

Choose a reason for hiding this comment

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

it should be independent with DependenciesMenu

return rootNode;
}

function parseTreeNodes(parentNode: TreeNode, treecontent: string, separator: string, indent: string, starter: string, eol: string): void {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of passing a parent node and modifying it, parseTreeNodes should directly parse a raw string and return an array of TreeNode.

const indent: string = " "; // three spaces
const separator: string = "\r\n";
const starter: string = "+- ";
const rootNode: TreeNode = new TreeNode("Dependencies");
Copy link
Member

Choose a reason for hiding this comment

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

The node new TreeNode("Dependencies") is dummy.
Can you simply return an array of top level dependencies here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants