From 6a4338d6910063a62d8cc6253ef8f6d474d58399 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 7 Jan 2021 16:22:31 +0800 Subject: [PATCH 1/3] feat: Add test metadata to the nodes --- .../jdtls/ext/core/PackageCommand.java | 4 +- src/utility.ts | 23 ++++++ src/views/PrimaryTypeNode.ts | 17 ++-- src/views/hierarchicalPackageNode.ts | 2 +- src/views/hierarchicalPackageRootNode.ts | 2 +- src/views/packageNode.ts | 13 ++- src/views/packageRootNode.ts | 25 ++++-- src/views/projectNode.ts | 2 +- test/suite/contextValue.test.ts | 79 ++++++++++++++----- tslint.json | 3 +- 10 files changed, 125 insertions(+), 45 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java index 72faf89b..1e5be9f1 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java @@ -303,11 +303,9 @@ private static List getPackageFragmentRoots(PackageParams query, IP IClasspathEntry resolvedClasspathEntry = fragmentRoot.getResolvedClasspathEntry(); if (resolvedClasspathEntry != null) { - Map attributes = new HashMap<>(); for (IClasspathAttribute attribute : resolvedClasspathEntry.getExtraAttributes()) { - attributes.put(attribute.getName(), attribute.getValue()); + node.setMetaDataValue(attribute.getName(), attribute.getValue()); } - node.setAttributes(attributes); } children.add(node); diff --git a/src/utility.ts b/src/utility.ts index 6f0a654f..6d0b5df5 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -3,6 +3,7 @@ import { Uri, window, workspace, WorkspaceFolder } from "vscode"; import { setUserError } from "vscode-extension-telemetry-wrapper"; +import { INodeData } from "./java/nodeData"; import { languageServerApiManager } from "./languageServerApi/languageServerApiManager"; import { Settings } from "./settings"; @@ -95,3 +96,25 @@ const identifierRegExp: RegExp = /^([a-zA-Z_$][a-zA-Z\d_$]*)$/; export function isJavaIdentifier(identifier: string): boolean { return identifierRegExp.test(identifier); } + +export function isTest(nodeData: INodeData | undefined): boolean { + if (!nodeData) { + return false; + } + + if (nodeData.metaData?.test === "true") { + return true; + } + + const mavenScope: string = nodeData.metaData?.["maven.scope"] || ""; + if (mavenScope.toLocaleLowerCase().includes("test")) { + return true; + } + + const gradleScope: string = nodeData.metaData?.gradle_scope || ""; + if (gradleScope.toLocaleLowerCase().includes("test")) { + return true; + } + + return false; +} diff --git a/src/views/PrimaryTypeNode.ts b/src/views/PrimaryTypeNode.ts index 063cec2e..0f0e6693 100644 --- a/src/views/PrimaryTypeNode.ts +++ b/src/views/PrimaryTypeNode.ts @@ -7,6 +7,7 @@ import { Commands } from "../commands"; import { Explorer } from "../constants"; import { INodeData, TypeKind } from "../java/nodeData"; import { Settings } from "../settings"; +import { isTest } from "../utility"; import { DataNode } from "./dataNode"; import { DocumentSymbolNode } from "./documentSymbolNode"; import { ExplorerNode } from "./explorerNode"; @@ -15,7 +16,7 @@ export class PrimaryTypeNode extends DataNode { public static K_TYPE_KIND = "TypeKind"; - constructor(nodeData: INodeData, parent: DataNode) { + constructor(nodeData: INodeData, parent: DataNode, protected _rootNode?: DataNode) { super(nodeData, parent); } @@ -91,15 +92,21 @@ export class PrimaryTypeNode extends DataNode { } protected get contextValue(): string { - const context = Explorer.ContextValueType.Type; + let contextValue: string = Explorer.ContextValueType.Type; const type = this.nodeData.metaData?.[PrimaryTypeNode.K_TYPE_KIND]; if (type === TypeKind.Enum) { - return `${context}+enum`; + contextValue += "+enum"; } else if (type === TypeKind.Interface) { - return `${context}+interface`; + contextValue += "+interface"; } else { - return `${context}+class`; + contextValue += "+class"; } + + if (isTest(this._rootNode?.nodeData)) { + contextValue += "+test"; + } + + return contextValue; } } diff --git a/src/views/hierarchicalPackageNode.ts b/src/views/hierarchicalPackageNode.ts index 32cd4bfc..e187999c 100644 --- a/src/views/hierarchicalPackageNode.ts +++ b/src/views/hierarchicalPackageNode.ts @@ -72,7 +72,7 @@ export class HierarchicalPackageNode extends PackageNode { result.push(new HierarchicalPackageNode(nodeData, this, this._project, this._rootNode)); } else if (nodeData.kind === NodeKind.PrimaryType) { if (nodeData.metaData && nodeData.metaData[PrimaryTypeNode.K_TYPE_KIND]) { - result.push(new PrimaryTypeNode(nodeData, this)); + result.push(new PrimaryTypeNode(nodeData, this, this._rootNode)); } } }); diff --git a/src/views/hierarchicalPackageRootNode.ts b/src/views/hierarchicalPackageRootNode.ts index cade2a91..ea9db3ab 100644 --- a/src/views/hierarchicalPackageRootNode.ts +++ b/src/views/hierarchicalPackageRootNode.ts @@ -41,7 +41,7 @@ export class HierarchicalPackageRootNode extends PackageRootNode { result.push(new FolderNode(data, this, this._project, this)); } else if (data.kind === NodeKind.PrimaryType) { if (data.metaData && data.metaData[PrimaryTypeNode.K_TYPE_KIND]) { - result.push(new PrimaryTypeNode(data, this)); + result.push(new PrimaryTypeNode(data, this, this)); } } }); diff --git a/src/views/packageNode.ts b/src/views/packageNode.ts index 6709666d..da05fbb8 100644 --- a/src/views/packageNode.ts +++ b/src/views/packageNode.ts @@ -6,6 +6,7 @@ import { Explorer } from "../constants"; import { Jdtls } from "../java/jdtls"; import { INodeData, NodeKind } from "../java/nodeData"; import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData"; +import { isTest } from "../utility"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; import { FileNode } from "./fileNode"; @@ -34,7 +35,7 @@ export class PackageNode extends DataNode { result.push(new FileNode(nodeData, this)); } else if (nodeData.kind === NodeKind.PrimaryType) { if (nodeData.metaData && nodeData.metaData[PrimaryTypeNode.K_TYPE_KIND]) { - result.push(new PrimaryTypeNode(nodeData, this)); + result.push(new PrimaryTypeNode(nodeData, this, this._rootNode)); } } }); @@ -48,11 +49,15 @@ export class PackageNode extends DataNode { protected get contextValue(): string | undefined { const parentData = this._rootNode.nodeData; + let contextValue: string = Explorer.ContextValueType.Package; if (parentData.entryKind === PackageRootKind.K_SOURCE || parentData.kind === NodeKind.Project) { - return `${Explorer.ContextValueType.Package}+source`; + contextValue += "+source"; } else if (parentData.entryKind === PackageRootKind.K_BINARY) { - return `${Explorer.ContextValueType.Package}+binary`; + contextValue += "+binary"; } - return undefined; + if (isTest(parentData)) { + contextValue += "+test"; + } + return contextValue; } } diff --git a/src/views/packageRootNode.ts b/src/views/packageRootNode.ts index 0c37cd6a..ef98f72e 100644 --- a/src/views/packageRootNode.ts +++ b/src/views/packageRootNode.ts @@ -7,6 +7,7 @@ import { Jdtls } from "../java/jdtls"; import { INodeData, NodeKind } from "../java/nodeData"; import { IPackageRootNodeData, PackageRootKind } from "../java/packageRootNodeData"; import { Settings } from "../settings"; +import { isTest } from "../utility"; import { ContainerNode } from "./containerNode"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; @@ -45,7 +46,7 @@ export class PackageRootNode extends DataNode { result.push(new FolderNode(data, this, this._project, this)); } else if (data.kind === NodeKind.PrimaryType) { if (data.metaData && data.metaData[PrimaryTypeNode.K_TYPE_KIND]) { - result.push(new PrimaryTypeNode(data, this)); + result.push(new PrimaryTypeNode(data, this, this)); } } }); @@ -64,20 +65,28 @@ export class PackageRootNode extends DataNode { protected get contextValue(): string { const data = this.nodeData; + let contextValue: string; if (data.entryKind === PackageRootKind.K_BINARY) { - let contextValue: string = Explorer.ContextValueType.Jar; + contextValue = Explorer.ContextValueType.Jar; const parent = this.getParent(); if (parent.path?.startsWith("REFERENCED_LIBRARIES_PATH")) { contextValue += "+referencedLibrary"; } return contextValue; - } else if (resourceRoots.includes(this._nodeData.name)) { - // APIs in JDT does not have a consistent result telling whether a package root - // is a source root or resource root, so we hard code some common resources root - // here as a workaround. - return `${Explorer.ContextValueType.PackageRoot}+resource`; } else { - return `${Explorer.ContextValueType.PackageRoot}+source`; + contextValue = Explorer.ContextValueType.PackageRoot; + if (isTest(data)) { + contextValue += "+test"; + } + if (resourceRoots.includes(this._nodeData.name)) { + // APIs in JDT does not have a consistent result telling whether a package root + // is a source root or resource root, so we hard code some common resources root + // here as a workaround. + contextValue += "+resource"; + } else { + contextValue += "+source"; + } + return contextValue; } } diff --git a/src/views/projectNode.ts b/src/views/projectNode.ts index 7673b22d..703b00f3 100644 --- a/src/views/projectNode.ts +++ b/src/views/projectNode.ts @@ -94,7 +94,7 @@ export class ProjectNode extends DataNode { // For invisible project with empty named package root with a default package, // types will be the project node's children if (data.metaData && data.metaData[PrimaryTypeNode.K_TYPE_KIND]) { - result.push(new PrimaryTypeNode(data, this)); + result.push(new PrimaryTypeNode(data, this, undefined)); } } }); diff --git a/test/suite/contextValue.test.ts b/test/suite/contextValue.test.ts index 42c2579c..77b48bf6 100644 --- a/test/suite/contextValue.test.ts +++ b/test/suite/contextValue.test.ts @@ -11,75 +11,87 @@ import { ContainerNode, FileNode, FolderNode, INodeData, NodeKind, PackageNode, suite("Context Value Tests", () => { test("test workspace node", async function() { - assert.equal((await workspace.getTreeItem()).contextValue, "java:workspaceFolder+uri"); + assert.ok(/java:workspaceFolder(?=.*?\b\+uri\b)/.test((await workspace.getTreeItem()).contextValue || "")); }); test("test Maven project node", async function() { - assert.equal((await mavenProject.getTreeItem()).contextValue, "java:project+java+maven+uri"); + assert.ok(/java:project(?=.*?\b\+java\b)(?=.*?\b\+maven\b)(?=.*?\b\+uri\b)/.test((await mavenProject.getTreeItem()).contextValue || "")); }); test("test Gradle project node", async function() { - assert.equal((await gradleProject.getTreeItem()).contextValue, "java:project+java+gradle+uri"); + assert.ok(/java:project(?=.*?\b\+java\b)(?=.*?\b\+gradle\b)(?=.*?\b\+uri\b)/.test((await gradleProject.getTreeItem()).contextValue || "")); }); test("test JRE container node", async function() { - assert.equal((await jreContainer.getTreeItem()).contextValue, "java:container+jre+uri"); + assert.ok(/java:container(?=.*?\b\+jre\b)(?=.*?\b\+uri\b)/.test((await jreContainer.getTreeItem()).contextValue || "")); }); test("test Maven container node", async function() { - assert.equal((await mavenContainer.getTreeItem()).contextValue, "java:container+maven+uri"); + assert.ok(/java:container(?=.*?\b\+maven\b)(?=.*?\b\+uri\b)/.test((await mavenContainer.getTreeItem()).contextValue || "")); }); test("test Gradle container node", async function() { - assert.equal((await gradleContainer.getTreeItem()).contextValue, "java:container+gradle+uri"); + assert.ok(/java:container(?=.*?\b\+gradle\b)(?=.*?\b\+uri\b)/.test((await gradleContainer.getTreeItem()).contextValue || "")); }); test("test Referenced Libraries container node", async function() { - assert.equal((await referencedLibrariesContainer.getTreeItem()).contextValue, "java:container+referencedLibrary+uri"); + assert.ok(/java:container(?=.*?\b\+referencedLibrary\b)(?=.*?\b\+uri\b)/.test((await referencedLibrariesContainer.getTreeItem()).contextValue || "")); }); test("test source root node", async function() { - assert.equal((await sourceRoot.getTreeItem()).contextValue, "java:packageRoot+source+uri"); + assert.ok(/java:packageRoot(?=.*?\b\+source\b)(?=.*?\b\+uri\b)/.test((await sourceRoot.getTreeItem()).contextValue || "")); + }); + + test("test test-source root node", async function() { + assert.ok(/java:packageRoot(?=.*?\b\+source\b)(?=.*?\b\+uri\b)(?=.*?\b\+test\b)/.test((await testSourceRoot.getTreeItem()).contextValue || "")); }); test("test resource root node", async function() { - assert.equal((await resourceRoot.getTreeItem()).contextValue, "java:packageRoot+resource+uri"); + assert.ok(/java:packageRoot(?=.*?\b\+resource\b)(?=.*?\b\+uri\b)/.test((await resourceRoot.getTreeItem()).contextValue || "")); }); test("test dependency jar node", async function() { - assert.equal((await dependencyJar.getTreeItem()).contextValue, "java:jar+uri"); + assert.ok(/java:jar(?=.*?\b\+uri\b)/.test((await dependencyJar.getTreeItem()).contextValue || "")); }); test("test referenced library jar node", async function() { - assert.equal((await referencedLibraryJar.getTreeItem()).contextValue, "java:jar+referencedLibrary+uri"); + assert.ok(/java:jar(?=.*?\b\+referencedLibrary\b)(?=.*?\b\+uri\b)/.test((await referencedLibraryJar.getTreeItem()).contextValue || "")); }); test("test source package node", async function() { - assert.equal((await sourcePackage.getTreeItem()).contextValue, "java:package+source+uri"); + assert.ok(/java:package(?=.*?\b\+source\b)(?=.*?\b\+uri\b)/.test((await sourcePackage.getTreeItem()).contextValue || "")); + }); + + test("test source package node", async function() { + assert.ok(/java:package(?=.*?\b\+source\b)(?=.*?\b\+test\b)(?=.*?\b\+uri\b)/.test((await testSourcePackage.getTreeItem()).contextValue || "")); }); test("test binary package node", async function() { - assert.equal((await binaryPackage.getTreeItem()).contextValue, "java:package+binary+uri"); + assert.ok(/java:package(?=.*?\b\+binary\b)(?=.*?\b\+uri\b)/.test((await binaryPackage.getTreeItem()).contextValue || "")); }); test("test file node", async function() { - assert.equal((await file.getTreeItem()).contextValue, "java:file+uri"); + assert.ok(/java:file(?=.*?\b\+uri\b)/.test((await file.getTreeItem()).contextValue || "")); }); test("test class type node", async function() { - assert.equal((await classType.getTreeItem()).contextValue, "java:type+class+uri"); + assert.ok(/java:type(?=.*?\b\+class\b)(?=.*?\b\+uri\b)/.test((await classType.getTreeItem()).contextValue || "")); + }); + + test("test test-class type node", async function() { + assert.ok(/java:type(?=.*?\b\+class\b)(?=.*?\b\+test\b)(?=.*?\b\+uri\b)/.test((await testClassType.getTreeItem()).contextValue || "")); }); test("test enum type node", async function() { - assert.equal((await enumType.getTreeItem()).contextValue, "java:type+enum+uri"); + assert.ok(/java:type(?=.*?\b\+enum\b)(?=.*?\b\+uri\b)/.test((await enumType.getTreeItem()).contextValue || "")); }); test("test interface type node", async function() { - assert.equal((await interfaceType.getTreeItem()).contextValue, "java:type+interface+uri"); + assert.ok(/java:type(?=.*?\b\+interface\b)(?=.*?\b\+uri\b)/.test((await interfaceType.getTreeItem()).contextValue || "")); }); test("test folder node", async function() { - assert.equal((await folder.getTreeItem()).contextValue, "java:folder+uri"); + assert.ok(/java:folder(?=.*?\b\+uri\b)/.test((await folder.getTreeItem()).contextValue || "")); }); }); @@ -143,6 +155,16 @@ const sourceRoot: PackageRootNode = new PackageRootNode({ entryKind: PackageRootKind.K_SOURCE, } as INodeData, mavenContainer, mavenProject); +const testSourceRoot: PackageRootNode = new PackageRootNode({ + name: "src/main/java", + uri: Uri.file(__dirname).toString(), + kind: NodeKind.PackageRoot, + entryKind: PackageRootKind.K_SOURCE, + metaData: { + test: "true" + } +} as INodeData, mavenContainer, mavenProject); + const resourceRoot: PackageRootNode = new PackageRootNode({ name: "src/main/resources", uri: Uri.file(__dirname).toString(), @@ -170,6 +192,12 @@ const sourcePackage: PackageNode = new PackageNode({ kind: NodeKind.Package, }, sourceRoot, mavenProject, sourceRoot); +const testSourcePackage: PackageNode = new PackageNode({ + name: "com.microsoft.java", + uri: Uri.file(__dirname).toString(), + kind: NodeKind.Package, +}, testSourceRoot, mavenProject, testSourceRoot); + const binaryPackage: PackageNode = new PackageNode({ name: "junit", uri: Uri.file(__dirname).toString(), @@ -189,7 +217,16 @@ const classType: PrimaryTypeNode = new PrimaryTypeNode({ metaData: { TypeKind: TypeKind.Class, }, -}, sourcePackage); +}, sourcePackage, sourceRoot); + +const testClassType: PrimaryTypeNode = new PrimaryTypeNode({ + name: "App", + uri: Uri.file(__dirname).toString(), + kind: NodeKind.PrimaryType, + metaData: { + TypeKind: TypeKind.Class, + }, +}, testSourcePackage, testSourceRoot); const enumType: PrimaryTypeNode = new PrimaryTypeNode({ name: "LanguageServerMode", @@ -198,7 +235,7 @@ const enumType: PrimaryTypeNode = new PrimaryTypeNode({ metaData: { TypeKind: TypeKind.Enum, }, -}, sourcePackage); +}, sourcePackage, sourceRoot); const interfaceType: PrimaryTypeNode = new PrimaryTypeNode({ name: "Controller", @@ -207,7 +244,7 @@ const interfaceType: PrimaryTypeNode = new PrimaryTypeNode({ metaData: { TypeKind: TypeKind.Interface, }, -}, sourcePackage); +}, sourcePackage, sourceRoot); const folder: FolderNode = new FolderNode({ name: "static", diff --git a/tslint.json b/tslint.json index 0bd73a41..d0ca6c55 100644 --- a/tslint.json +++ b/tslint.json @@ -39,6 +39,7 @@ "log", "error" ], - "no-namespace": false + "no-namespace": false, + "prefer-conditional-expression": false } } From f31b80b1a386aac52c583302fe182682df30231e Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Thu, 7 Jan 2021 16:37:07 +0800 Subject: [PATCH 2/3] fix tslint violation --- test/suite/contextValue.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/suite/contextValue.test.ts b/test/suite/contextValue.test.ts index 77b48bf6..7cf976a8 100644 --- a/test/suite/contextValue.test.ts +++ b/test/suite/contextValue.test.ts @@ -35,7 +35,8 @@ suite("Context Value Tests", () => { }); test("test Referenced Libraries container node", async function() { - assert.ok(/java:container(?=.*?\b\+referencedLibrary\b)(?=.*?\b\+uri\b)/.test((await referencedLibrariesContainer.getTreeItem()).contextValue || "")); + assert.ok(/java:container(?=.*?\b\+referencedLibrary\b)(?=.*?\b\+uri\b)/ + .test((await referencedLibrariesContainer.getTreeItem()).contextValue || "")); }); test("test source root node", async function() { @@ -43,7 +44,8 @@ suite("Context Value Tests", () => { }); test("test test-source root node", async function() { - assert.ok(/java:packageRoot(?=.*?\b\+source\b)(?=.*?\b\+uri\b)(?=.*?\b\+test\b)/.test((await testSourceRoot.getTreeItem()).contextValue || "")); + assert.ok(/java:packageRoot(?=.*?\b\+source\b)(?=.*?\b\+uri\b)(?=.*?\b\+test\b)/ + .test((await testSourceRoot.getTreeItem()).contextValue || "")); }); test("test resource root node", async function() { @@ -63,7 +65,8 @@ suite("Context Value Tests", () => { }); test("test source package node", async function() { - assert.ok(/java:package(?=.*?\b\+source\b)(?=.*?\b\+test\b)(?=.*?\b\+uri\b)/.test((await testSourcePackage.getTreeItem()).contextValue || "")); + assert.ok(/java:package(?=.*?\b\+source\b)(?=.*?\b\+test\b)(?=.*?\b\+uri\b)/ + .test((await testSourcePackage.getTreeItem()).contextValue || "")); }); test("test binary package node", async function() { @@ -161,8 +164,8 @@ const testSourceRoot: PackageRootNode = new PackageRootNode({ kind: NodeKind.PackageRoot, entryKind: PackageRootKind.K_SOURCE, metaData: { - test: "true" - } + test: "true", + }, } as INodeData, mavenContainer, mavenProject); const resourceRoot: PackageRootNode = new PackageRootNode({ From 3183a45a67d231057df3493e81ff5a876f558232 Mon Sep 17 00:00:00 2001 From: Sheng Chen Date: Fri, 8 Jan 2021 10:35:29 +0800 Subject: [PATCH 3/3] Add comments to the test suite --- test/suite/contextValue.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/suite/contextValue.test.ts b/test/suite/contextValue.test.ts index 7cf976a8..4befcc60 100644 --- a/test/suite/contextValue.test.ts +++ b/test/suite/contextValue.test.ts @@ -8,6 +8,12 @@ import { ContainerNode, FileNode, FolderNode, INodeData, NodeKind, PackageNode, // tslint:disable: only-arrow-functions // tslint:disable: no-object-literal-type-assertion + +/** + * This suite is to test the context value of different nodes in the explorer, + * Users can register their commands to the nodes by writing RegExp to match the metadata. + * More details, please see: https://github.com/microsoft/vscode-java-dependency/wiki/Register-Command-onto-the-Nodes-of-Project-View + */ suite("Context Value Tests", () => { test("test workspace node", async function() {