Add go-to-effective command to conflict dependencies (#716)

This commit is contained in:
Melody618 2021-08-30 17:13:06 +08:00 коммит произвёл GitHub
Родитель ebe8c8034e
Коммит 60c5412d67
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 120 добавлений и 32 удалений

Просмотреть файл

@ -210,6 +210,11 @@
"category": "Maven",
"icon": "$(wrench)"
},
{
"command": "maven.dependency.goToEffective",
"title": "%contributes.commands.maven.dependency.goToEffective%",
"category": "Maven"
},
{
"command": "maven.project.goToDefinition",
"title": "%contributes.commands.maven.project.goToDefinition%",
@ -321,6 +326,10 @@
"command": "maven.project.setDependencyVersion",
"when": "never"
},
{
"command": "maven.dependency.goToEffective",
"when": "never"
},
{
"command": "maven.project.goToDefinition",
"when": "never"
@ -457,6 +466,11 @@
"when": "view == mavenProjects && viewItem =~ /maven:dependency(?=.*?\\b\\+conflict\\b)/",
"group": "inline"
},
{
"command": "maven.dependency.goToEffective",
"when": "view == mavenProjects && viewItem =~ /maven:dependency(?=.*?\\b\\+conflict\\b)/",
"group": "0-dependency@2"
},
{
"command": "maven.project.goToDefinition",
"when": "view == mavenProjects && viewItem =~ /maven:dependency/",

Просмотреть файл

@ -14,9 +14,10 @@
"contributes.commands.maven.view.hierarchical": "Switch to hierarchical view",
"contributes.commands.maven.view.flat": "Switch to flat view",
"contributes.commands.maven.project.addDependency": "Add a dependency...",
"contributes.commands.maven.project.showDependencies": "Show dependencies",
"contributes.commands.maven.project.excludeDependency": "Exclude dependency",
"contributes.commands.maven.project.setDependencyVersion": "Resolve conflict...",
"contributes.commands.maven.project.showDependencies": "Show Dependencies",
"contributes.commands.maven.project.excludeDependency": "Exclude Dependency",
"contributes.commands.maven.project.setDependencyVersion": "Resolve Conflict...",
"contributes.commands.maven.dependency.goToEffective": "Go to Effective Dependency",
"contributes.commands.maven.project.goToDefinition": "Go to Definition",
"contributes.views.explorer.mavenProjects": "Maven",
"contributes.viewsWelcome.mavenProjects.untrustedWorkspaces": "Advanced features (e.g. executing lifecycle phases and plugin goals) are disabled in Restricted Mode for security concern.\nPOM editing assistance (e.g. [add a dependency](command:maven.project.addDependency)) is still available.\nLearn more about [Workspace Trust](https://aka.ms/vscode-workspace-trust).\n[Manage Workspace Trust](command:workbench.action.manageTrust)",

Просмотреть файл

@ -18,6 +18,7 @@
"contributes.commands.maven.project.goToDefinition": "转到定义",
"contributes.commands.maven.project.excludeDependency": "删除依赖",
"contributes.commands.maven.project.setDependencyVersion": "指定依赖版本为...",
"contributes.commands.maven.dependency.goToEffective": "转到有效的依赖项",
"contributes.views.explorer.mavenProjects": "Maven",
"configuration.maven.excludedFolders": "指定搜索 Maven 项目时要排除的文件夹。",
"configuration.maven.executable.preferMavenWrapper": "指定是否优先使用 Maven Wrapper。如果为 true则尝试向上遍历父文件夹寻找 mvnw 作为可执行文件;如果为 false或者找不到 mvnw则尝试使用系统 PATH 中的 mvn。",

Просмотреть файл

@ -66,7 +66,7 @@ class DiagnosticProvider {
}
public async createDiagnostics(node: Dependency): Promise<vscode.Diagnostic> {
const root: Dependency = <Dependency> node.root;
const root: Dependency = node.root;
const range: vscode.Range = await this.findConflictRange(root.projectPomPath, root.groupId, root.artifactId);
const message: string = `Dependency conflict in ${root.artifactId}: ${node.groupId}:${node.artifactId}:${node.version} conflict with ${node.omittedStatus?.effectiveVersion}`;
const diagnostic: vscode.Diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Warning);

Просмотреть файл

@ -4,6 +4,7 @@
import { TreeDataProvider } from "vscode";
import * as vscode from "vscode";
import { Utils } from "../utils/Utils";
import { Dependency } from "./model/Dependency";
import { ITreeItem } from "./model/ITreeItem";
import { MavenProject } from "./model/MavenProject";
import { WorkspaceFolder } from "./model/WorkspaceFolder";
@ -80,6 +81,13 @@ class MavenExplorerProvider implements TreeDataProvider<ITreeItem> {
return element.getChildren ? element.getChildren() : undefined;
}
}
public async getParent(element: ITreeItem): Promise<ITreeItem | undefined> {
if (element instanceof Dependency) {
return element.parent;
} else {
return undefined;
}
}
public refresh(item?: ITreeItem): void {
this._onDidChangeTreeData.fire(item);

Просмотреть файл

@ -3,10 +3,10 @@
import * as vscode from "vscode";
import { ITreeItem } from "./ITreeItem";
import { ITreeNode } from "./ITreeNode";
import { IOmittedStatus } from "./OmittedStatus";
import { TreeNode } from "./TreeNode";
export class Dependency extends TreeNode implements ITreeItem {
export class Dependency implements ITreeItem, ITreeNode {
public fullArtifactName: string = ""; // groupId:artifactId:version:scope
public projectPomPath: string;
public groupId: string;
@ -15,8 +15,10 @@ export class Dependency extends TreeNode implements ITreeItem {
public scope: string;
public omittedStatus?: IOmittedStatus;
public uri: vscode.Uri;
public children: Dependency[] = [];
public root: Dependency;
public parent: Dependency;
constructor(gid: string, aid: string, version: string, scope: string, projectPomPath: string, omittedStatus?: IOmittedStatus) {
super();
this.groupId = gid;
this.artifactId = aid;
this.version = version;
@ -26,8 +28,13 @@ export class Dependency extends TreeNode implements ITreeItem {
this.omittedStatus = omittedStatus;
}
public addChild(node: Dependency): void {
node.parent = this;
this.children.push(node);
}
public getContextValue(): string {
const root = <Dependency> this.root;
const root = this.root;
let contextValue: string = "maven:dependency";
if (root.fullArtifactName === this.fullArtifactName) {
contextValue = `${contextValue}+root`;

Просмотреть файл

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export interface ITreeNode {
children: ITreeNode[];
parent?: ITreeNode | undefined;
root?: ITreeNode | undefined;
addChild(node: ITreeNode): void;
}

Просмотреть файл

@ -26,6 +26,7 @@ export class MavenProject implements ITreeItem {
public pomPath: string;
public _fullDependencyText: string;
public conflictNodes: Dependency[];
public dependencyNodes: Dependency[];
private ePomProvider: EffectivePomProvider;
private _ePom: any;
private _pom: any;

Просмотреть файл

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export class TreeNode {
public children: TreeNode[] = [];
public parent?: TreeNode | undefined;
public root?: TreeNode | undefined;
public addChild(node: TreeNode): void {
node.parent = this;
this.children.push(node);
}
public addChildren(nodes: TreeNode[]): void {
nodes.forEach(node => node.parent = this);
this.children = this.children.concat(nodes);
}
}

Просмотреть файл

@ -17,6 +17,7 @@ import { diagnosticProvider } from "./DiagnosticProvider";
import { initExpService } from "./experimentationService";
import { decorationProvider } from "./explorer/decorationProvider";
import { mavenExplorerProvider } from "./explorer/mavenExplorerProvider";
import { Dependency } from "./explorer/model/Dependency";
import { ITreeItem } from "./explorer/model/ITreeItem";
import { MavenProject } from "./explorer/model/MavenProject";
import { PluginGoal } from "./explorer/model/PluginGoal";
@ -24,6 +25,7 @@ import { pluginInfoProvider } from "./explorer/pluginInfoProvider";
import { addDependencyHandler } from "./handlers/addDependencyHandler";
import { debugHandler } from "./handlers/debugHandler";
import { excludeDependencyHandler } from "./handlers/excludeDependencyHandler";
import { goToEffectiveHandler } from "./handlers/goToEffectiveHandler";
import { jumpToDefinitionHandler } from "./handlers/jumpToDefinitionHandler";
import { runFavoriteCommandsHandler } from "./handlers/runFavoriteCommandsHandler";
import { setDependencyVersionHandler } from "./handlers/setDependencyVersionHandler";
@ -59,7 +61,9 @@ async function doActivate(_operationId: string, context: vscode.ExtensionContext
await vscode.commands.executeCommand("setContext", "vscode-maven:activated", true);
// register tree view
await mavenExplorerProvider.loadProjects();
context.subscriptions.push(vscode.window.createTreeView("mavenProjects", { treeDataProvider: mavenExplorerProvider, showCollapseAll: true }));
const view = vscode.window.createTreeView("mavenProjects", { treeDataProvider: mavenExplorerProvider, showCollapseAll: true });
context.subscriptions.push(view);
registerCommand(context, "maven.dependency.goToEffective", (node?: Dependency) => goToEffectiveHandler(view, node));
context.subscriptions.push(vscode.workspace.onDidGrantWorkspaceTrust(_e => {
mavenExplorerProvider.refresh();
}));

Просмотреть файл

@ -12,7 +12,7 @@ export async function excludeDependencyHandler(toExclude?: Dependency): Promise<
if (toExclude === undefined) {
throw new UserError("Only Dependency can be excluded.");
}
const root: Dependency | undefined = toExclude.root ? <Dependency> toExclude.root : undefined;
const root: Dependency = toExclude.root;
if (root === undefined || toExclude.fullArtifactName === root.fullArtifactName) {
vscode.window.showInformationMessage("The dependency written in pom can not be excluded.");
return;

Просмотреть файл

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as vscode from "vscode";
import { mavenExplorerProvider } from "../explorer/mavenExplorerProvider";
import { Dependency } from "../explorer/model/Dependency";
import { ITreeItem } from "../explorer/model/ITreeItem";
import { MavenProject } from "../explorer/model/MavenProject";
import { Queue } from "../taskExecutor";
export async function goToEffectiveHandler(view: vscode.TreeView<ITreeItem>, node?: Dependency): Promise<void> {
if (node === undefined || node.omittedStatus === undefined) {
throw new Error("No conflict dependency node specified.");
}
const fullArtifactName: string = [node.groupId, node.artifactId, node.omittedStatus.effectiveVersion, node.scope].join(":");
const pomPath: string = node.projectPomPath;
const project: MavenProject | undefined = mavenExplorerProvider.getMavenProject(pomPath);
if (project === undefined) {
throw new Error("Failed to find maven projects.");
}
const dependencyNodes = project.dependencyNodes;
const treeItem: Dependency | undefined = await searchFirstEffective(dependencyNodes, fullArtifactName);
if (treeItem === undefined) {
throw new Error("Failed to find dependency.");
}
view.reveal(treeItem, { focus: true});
}
async function searchFirstEffective(dependencyNodes: Dependency[], fullArtifactName: string): Promise<Dependency | undefined> {
let targetItem: Dependency | undefined;
const queue: Queue<Dependency> = new Queue();
for (const child of dependencyNodes) {
queue.push(child);
}
while (queue.empty() === false) {
const node: Dependency | undefined = queue.pop();
if (node === undefined) {
throw new Error("Failed to find dependency.");
}
if (node.fullArtifactName === fullArtifactName) {
targetItem = node;
break;
}
const children = <Dependency[]> node.children;
for (const child of children) {
queue.push(child);
}
}
return targetItem;
}

Просмотреть файл

@ -16,7 +16,7 @@ export async function jumpToDefinitionHandler(node?: Dependency): Promise<void>
if (node.parent === undefined) {
selectedPath = node.projectPomPath;
} else {
const parent: Dependency = <Dependency> node.parent;
const parent: Dependency = node.parent;
selectedPath = localPomPath(parent.groupId, parent.artifactId, parent.version);
}
await goToDefinition(selectedPath, node.groupId, node.artifactId);

Просмотреть файл

@ -32,6 +32,7 @@ export async function parseRawDependencyDataHandler(project: MavenProject): Prom
const prefix: string = "+- ";
const [treeNodes, conflictNodes] = await parseTreeNodes(treeContent, eol, indent, prefix, project.pomPath);
project.conflictNodes = conflictNodes;
project.dependencyNodes = treeNodes;
return treeNodes;
}
@ -86,7 +87,7 @@ async function parseTreeNodes(treecontent: string, eol: string, indent: string,
} else {
const level: number = (preIndentCnt - curIndentCnt) / indent.length;
for (let i = level; i > 0; i -= 1) {
parentNode = <Dependency> parentNode.parent;
parentNode = parentNode.parent;
}
parentNode.addChild(curNode);
}
@ -103,7 +104,7 @@ async function parseTreeNodes(treecontent: string, eol: string, indent: string,
// find all parent and set hasConflict upforward
let tmpNode = curNode;
while (tmpNode.parent !== undefined) {
const parent = <Dependency> tmpNode.parent;
const parent = tmpNode.parent;
if (parent.uri.query !== "hasConflict") {
parent.uri = uri.with({query: "hasConflict"});
tmpNode = parent;

Просмотреть файл

@ -3,7 +3,7 @@
import { Disposable } from "vscode";
class Queue<T> {
export class Queue<T> {
private _store: T[] = [];
public push(val: T): void {
this._store.push(val);
@ -11,6 +11,12 @@ class Queue<T> {
public pop(): T | undefined {
return this._store.shift();
}
public empty(): boolean {
if (this._store.length === 0) {
return true;
}
return false;
}
}
class TaskExecutor implements Disposable {