From b87c7b513a38d02034e8b76b8d4bbeb329476bac Mon Sep 17 00:00:00 2001 From: Ye Zhihao Date: Thu, 26 Dec 2019 14:01:13 +0800 Subject: [PATCH] Support right click context for viewer nodes (#208) * Support URI for nodes created from classpath entry * Support URI for PackageRootNode * Support URI for PackageFragment * Support Right Click Context in Client Side * Change description for `revealFileInOS` * Hide right-click-context commands from command palette * Add context value for testing uri * Extract detecting container uri as util method * Fix getContainerURI NPE --- .../microsoft/jdtls/ext/core/ExtUtils.java | 15 ++++++ .../jdtls/ext/core/PackageCommand.java | 20 +++++--- .../jdtls/ext/core/model/PackageNode.java | 30 +++++++----- .../jdtls/ext/core/model/PackageRootNode.java | 15 +++++- package.json | 48 ++++++++++++++++++- package.nls.json | 3 ++ package.nls.zh.json | 3 ++ src/commands.ts | 6 +++ src/views/dependencyDataProvider.ts | 6 +++ 9 files changed, 125 insertions(+), 21 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ExtUtils.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ExtUtils.java index 1f787dbc..431234bd 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ExtUtils.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ExtUtils.java @@ -20,13 +20,16 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IJarEntryResource; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.core.JarEntryDirectory; import org.eclipse.jdt.internal.core.JarEntryFile; +import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; public final class ExtUtils { @@ -104,6 +107,18 @@ public static IPath removeProjectSegment(String projectElementName, IPath path) return path; } + public static URI getContainerURI(IJavaProject javaProject, IClasspathContainer container) throws CoreException { + switch (container.getKind()) { + case IClasspathContainer.K_DEFAULT_SYSTEM: // JRE Container + case IClasspathContainer.K_SYSTEM: + return JavaRuntime.getVMInstall(javaProject).getInstallLocation().toURI(); + case IClasspathContainer.K_APPLICATION: // Plugin Container, Maven Container, etc + return null; // TODO: find out a good way to detect these containers' uri + default: // Persistent container (e.g. /src/main/java) + return container.getPath().toFile().toURI(); + } + } + private static JarEntryFile findFileInJar(JarEntryDirectory directory, String path) { for (IJarEntryResource child : directory.getChildren()) { if (child instanceof JarEntryFile && child.getFullPath().toPortableString().equals(path)) { 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 3130fda1..b3cb3e8b 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 @@ -175,9 +175,9 @@ public static List resolvePath(List arguments, IProgressMon IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) packageFragment.getParent(); PackageNode rootNode = null; - rootNode = new PackageRootNode( + rootNode = new PackageRootNode(pkgRoot, ExtUtils.removeProjectSegment(packageFragment.getJavaProject().getElementName(), pkgRoot.getPath()).toPortableString(), - pkgRoot.getPath().toPortableString(), NodeKind.PACKAGEROOT, pkgRoot.getKind()); + NodeKind.PACKAGEROOT); result.add(PackageNode.createNodeForProject(packageFragment)); result.add(rootNode); @@ -209,8 +209,8 @@ private static List getParentAncestorNodes(IResource element) throw IJavaElement javaElement = JavaCore.create(element); if (javaElement instanceof IPackageFragmentRoot) { IPackageFragmentRoot pkgRoot = (IPackageFragmentRoot) javaElement; - nodeList.add(0, new PackageRootNode(element.getProjectRelativePath().toPortableString(), pkgRoot.getPath().toPortableString(), - NodeKind.PACKAGEROOT, pkgRoot.getKind())); + nodeList.add(0, new PackageRootNode(pkgRoot, + element.getProjectRelativePath().toPortableString(), NodeKind.PACKAGEROOT)); nodeList.add(0, PackageNode.createNodeForProject(javaElement)); return nodeList; } else if (javaElement instanceof IPackageFragment) { @@ -280,8 +280,7 @@ private static List getPackageFragmentRoots(PackageParams query, IP if (fragmentRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { displayName = ExtUtils.removeProjectSegment(javaProject.getElementName(), fragmentRoot.getPath()).toPortableString(); } - PackageRootNode node = new PackageRootNode(displayName, fragmentRoot.getPath().toPortableString(), NodeKind.PACKAGEROOT, - fragmentRoot.getKind()); + PackageRootNode node = new PackageRootNode(fragmentRoot, displayName, NodeKind.PACKAGEROOT); children.add(node); if (fragmentRoot instanceof JrtPackageFragmentRoot) { node.setModuleName(fragmentRoot.getModuleDescription().getElementName()); @@ -440,7 +439,14 @@ private static List convertToPackageNode(Object[] rootContent, IPac List result = new ArrayList<>(); for (Object root : rootContent) { if (root instanceof IPackageFragment) { - result.add(PackageNode.createNodeForPackageFragment((IPackageFragment) root)); + IPackageFragment fragment = (IPackageFragment) root; + PackageNode entry = PackageNode.createNodeForPackageFragment(fragment); + if (fragment.getResource() != null) { + entry.setUri(fragment.getResource().getLocationURI().toString()); + } else { + entry.setUri(fragment.getPath().toFile().toURI().toString()); + } + result.add(entry); } else if (root instanceof IClassFile) { IClassFile classFile = (IClassFile) root; PackageNode entry = new PackageNode(classFile.getElementName(), null, NodeKind.TYPEROOT); diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageNode.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageNode.java index 2b17d375..2a363f7f 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageNode.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageNode.java @@ -11,6 +11,7 @@ package com.microsoft.jdtls.ext.core.model; +import java.net.URI; import java.util.List; import org.eclipse.core.resources.IFile; @@ -133,11 +134,11 @@ public static PackageNode createNodeForPackageFragmentRoot(IPackageFragmentRoot if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { return createNodeForClasspathVariable(entry); } else { - return new PackageRootNode(pkgRoot.getElementName(), pkgRoot.getPath().toPortableString(), NodeKind.PACKAGEROOT, pkgRoot.getKind()); + return new PackageRootNode(pkgRoot, pkgRoot.getElementName(), NodeKind.PACKAGEROOT); } } else { - return new PackageRootNode(ExtUtils.removeProjectSegment(pkgRoot.getJavaProject().getElementName(), pkgRoot.getPath()).toPortableString(), - pkgRoot.getPath().toPortableString(), NodeKind.PACKAGEROOT, pkgRoot.getKind()); + return new PackageRootNode(pkgRoot, + ExtUtils.removeProjectSegment(pkgRoot.getJavaProject().getElementName(), pkgRoot.getPath()).toPortableString(), NodeKind.PACKAGEROOT); } } @@ -161,17 +162,21 @@ public static PackageNode createNodeForClasspathEntry(IClasspathEntry classpathE container = JavaCore.getClasspathContainer(entry.getPath(), javaProject); } if (container != null) { - switch (nodeKind) { - case CONTAINER: - return new ContainerNode(container.getDescription(), container.getPath().toPortableString(), nodeKind, entry.getEntryKind()); - case PACKAGEROOT: + PackageNode node = null; + if (nodeKind == NodeKind.CONTAINER) { + node = new ContainerNode(container.getDescription(), container.getPath().toPortableString(), nodeKind, entry.getEntryKind()); + final URI containerURI = ExtUtils.getContainerURI(javaProject, container); + node.setUri(containerURI != null ? containerURI.toString() : null); + } else if (nodeKind == NodeKind.PACKAGEROOT) { // ClasspathEntry for referenced jar files // Use package name as package root name String[] pathSegments = container.getPath().segments(); - return new PackageRootNode(pathSegments[pathSegments.length - 1], container.getPath().toPortableString(), nodeKind, - IPackageFragmentRoot.K_BINARY); - default: - return null; + node = new PackageRootNode( + pathSegments[pathSegments.length - 1], + container.getPath().toPortableString(), + container.getPath().toFile().toURI().toString(), + nodeKind, IPackageFragmentRoot.K_BINARY); } + return node; } } catch (CoreException e) { JdtlsExtActivator.logException("Problems when convert classpath entry to package node ", e); @@ -201,7 +206,8 @@ public static PackageNode createNodeForClasspathVariable(IClasspathEntry classpa IClasspathEntry entry = JavaCore.getResolvedClasspathEntry(classpathEntry); String name = classpathEntry.getPath().toPortableString(); String path = entry.getPath().toPortableString(); - return new PackageRootNode(name, path, NodeKind.PACKAGEROOT, IPackageFragmentRoot.K_BINARY); + String uri = entry.getPath().toFile().toURI().toString(); + return new PackageRootNode(name, path, uri, NodeKind.PACKAGEROOT, IPackageFragmentRoot.K_BINARY); } public String getName() { diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageRootNode.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageRootNode.java index b172c327..d98439dd 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageRootNode.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/model/PackageRootNode.java @@ -13,17 +13,30 @@ import java.util.Map; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; + public class PackageRootNode extends PackageNode { private int entryKind; private Map attributes; - public PackageRootNode(String name, String path, NodeKind kind, int entryKind) { + public PackageRootNode(String name, String path, String uri, NodeKind kind, int entryKind) { super(name, path, kind); + this.setUri(uri); this.entryKind = entryKind; } + public PackageRootNode(IPackageFragmentRoot pkgRoot, String name, NodeKind kind) throws JavaModelException { + this(name, pkgRoot.getPath().toPortableString(), null, kind, pkgRoot.getKind()); + if (pkgRoot.getResource() != null) { + this.setUri(pkgRoot.getResource().getLocationURI().toString()); + } else { + this.setUri(pkgRoot.getPath().toFile().toURI().toString()); + } + } + public int getEntryType() { return this.entryKind; } diff --git a/package.json b/package.json index 38882567..d00a44d1 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "explorer" ], "engines": { - "vscode": "^1.30.0" + "vscode": "^1.31.0" }, "repository": { "type": "git", @@ -83,6 +83,21 @@ "dark": "images/dark/icon-link.svg", "light": "images/light/icon-link.svg" } + }, + { + "command": "java.view.package.revealFileInOS", + "title": "%contributes.commands.java.view.package.revealFileInOS%", + "category": "Java" + }, + { + "command": "java.view.package.copyFilePath", + "title": "%contributes.commands.java.view.package.copyFilePath%", + "category": "Java" + }, + { + "command": "java.view.package.copyRelativeFilePath", + "title": "%contributes.commands.java.view.package.copyRelativeFilePath%", + "category": "Java" } ], "configuration": { @@ -121,6 +136,20 @@ } }, "menus": { + "commandPalette": [ + { + "command": "java.view.package.revealFileInOS", + "when": "never" + }, + { + "command": "java.view.package.copyFilePath", + "when": "never" + }, + { + "command": "java.view.package.copyRelativeFilePath", + "when": "never" + } + ], "view/title": [ { "command": "java.view.package.refresh", @@ -147,6 +176,23 @@ "when": "view == javaDependencyExplorer && config.java.dependency.syncWithFolderExplorer == true", "group": "navigation@0" } + ], + "view/item/context": [ + { + "command": "java.view.package.revealFileInOS", + "when": "view == javaDependencyExplorer && viewItem =~ /java:.*?\\+uri/", + "group": "@1" + }, + { + "command": "java.view.package.copyFilePath", + "when": "view == javaDependencyExplorer && viewItem =~ /java:.*?\\+uri/", + "group": "@2" + }, + { + "command": "java.view.package.copyRelativeFilePath", + "when": "view == javaDependencyExplorer && viewItem =~ /java:.*?\\+uri/", + "group": "@2" + } ] }, "views": { diff --git a/package.nls.json b/package.nls.json index a8bcd4a3..5c252147 100644 --- a/package.nls.json +++ b/package.nls.json @@ -7,6 +7,9 @@ "contributes.commands.java.view.package.changeToHierarchicalPackageView":"Change to hierarchical package representation", "contributes.commands.java.view.package.linkWithFolderExplorer":"Synchronize dependency viewer selection with folder explorer", "contributes.commands.java.view.package.unlinkWithFolderExplorer":"Desynchronize dependency viewer selection with folder explorer", + "contributes.commands.java.view.package.revealFileInOS": "Reveal In Explorer", + "contributes.commands.java.view.package.copyFilePath": "Copy Path", + "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "configuration.java.dependency.title": "Java Dependency Configuration", "configuration.java.dependency.showOutline": "Enable show outline in the Java Dependency explorer", "configuration.java.dependency.syncWithFolderExplorer": "Synchronize dependency viewer selection with folder explorer", diff --git a/package.nls.zh.json b/package.nls.zh.json index 3c9c655a..ee0f600d 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -7,6 +7,9 @@ "contributes.commands.java.view.package.changeToHierarchicalPackageView":"将 Java 包显示方式切换为分层显示", "contributes.commands.java.view.package.linkWithFolderExplorer":"开启 Java 依赖项资源管理器与当前浏览文件的关联", "contributes.commands.java.view.package.unlinkWithFolderExplorer":"关闭 Java 依赖项资源管理器与当前浏览文件的关联", + "contributes.commands.java.view.package.revealFileInOS": "打开所在的文件夹", + "contributes.commands.java.view.package.copyFilePath": "复制路径", + "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", "configuration.java.dependency.showOutline": "在 Java 依赖项资源管理器中显示类成员大纲", "configuration.java.dependency.syncWithFolderExplorer": "在 Java 依赖项资源管理器中同步关联当前打开的文件", diff --git a/src/commands.ts b/src/commands.ts index 430fd354..1e49effe 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -24,6 +24,12 @@ export namespace Commands { export const VIEW_PACKAGE_OUTLINE = "java.view.package.outline"; + export const VIEW_PACKAGE_REVEAL_FILE_OS = "java.view.package.revealFileInOS"; + + export const VIEW_PACKAGE_COPY_FILE_PATH = "java.view.package.copyFilePath"; + + export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; + export const JAVA_PROJECT_CREATE = "java.project.create"; export const JAVA_PROJECT_LIST = "java.project.list"; diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index ebdabd75..107a04c7 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -28,6 +28,12 @@ export class DependencyDataProvider implements TreeDataProvider { constructor(public readonly context: ExtensionContext) { context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_REFRESH, (debounce?: boolean) => this.refreshWithLog(debounce))); + context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, + (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); + context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, + (node: INodeData) => commands.executeCommand("copyFilePath", Uri.parse(node.uri)))); + context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH, + (node: INodeData) => commands.executeCommand("copyRelativeFilePath", Uri.parse(node.uri)))); context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_OPEN_FILE, instrumentOperation(Commands.VIEW_PACKAGE_OPEN_FILE, (_operationId, uri) => this.openFile(uri)))); context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_OUTLINE,