From 6a928595988322c23dadf69bd45d96896515f611 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 23 Jun 2020 15:05:16 +0800 Subject: [PATCH 01/44] add getmainmethod command --- .../jdtls/ext/core/CommandHandler.java | 2 + .../jdtls/ext/core/ProjectCommand.java | 94 +++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java index 1e1fdef9..32f300f2 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java @@ -31,6 +31,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return PackageCommand.getChildren(arguments, monitor); case "java.resolvePath": return PackageCommand.resolvePath(arguments, monitor); + case "java.project.getMainMethod": + return ProjectCommand.getMainMethod(arguments, monitor); default: break; } diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index b5cf9427..24abd97c 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -19,18 +19,37 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.jdt.ls.core.internal.managers.UpdateClasspathJob; import org.eclipse.jdt.ls.core.internal.preferences.Preferences.ReferencedLibraries; import com.microsoft.jdtls.ext.core.model.NodeKind; import com.microsoft.jdtls.ext.core.model.PackageNode; + public final class ProjectCommand { public static List listProjects(List arguments, IProgressMonitor monitor) { @@ -79,4 +98,79 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { String fileName = workspacePath.toFile().getName(); return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } + + public static List getMainMethod(List arguments, IProgressMonitor monitor) throws JavaModelException { + final List res = new ArrayList<>(); + IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH| SearchPattern.R_CASE_SENSITIVE); + SearchRequestor requestor = new SearchRequestor() { + @Override + public void acceptSearchMatch(SearchMatch match) { + Object element = match.getElement(); + if (element instanceof IMethod) { + IMethod method = (IMethod) element; + try { + if (method.isMainMethod()) { + IResource resource = method.getResource(); + if (resource != null) { + IProject project = resource.getProject(); + if (project != null) { + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = getJavaProject(project); + if (javaProject != null) { + String moduleName = getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; + } + } + String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore + } + } + res.add(new PackageNode(mainClass, filePath, NodeKind.FILE)); + } + } + } + } catch (JavaModelException e) { + // ignore + } + } + } + }; + SearchEngine searchEngine = new SearchEngine(); + try { + searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, + scope, requestor, null /* progress monitor */); + } catch (Exception e) { + // ignore + } + return res; + } + + public static String getModuleName(IJavaProject project) { + if (project == null || !JavaRuntime.isModularProject(project)) { + return null; + } + IModuleDescription module; + try { + module = project.getModuleDescription(); + } catch (CoreException e) { + return null; + } + return module == null ? null : module.getElementName(); + } + + public static IJavaProject getJavaProject(IProject project) { + if (ProjectUtils.isJavaProject(project)) { + return JavaCore.create(project); + } + return null; + } + } From 3d85cf2f986f94dd2fca8a8ad839b5881d602137 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 23 Jun 2020 15:13:43 +0800 Subject: [PATCH 02/44] Update ProjectCommand.java --- .../src/com/microsoft/jdtls/ext/core/ProjectCommand.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index 24abd97c..428936fd 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -21,7 +21,6 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IContainer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; @@ -38,7 +37,6 @@ import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.launching.JavaRuntime; -import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; @@ -99,7 +97,7 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } - public static List getMainMethod(List arguments, IProgressMonitor monitor) throws JavaModelException { + public static List getMainMethod(List arguments, IProgressMonitor monitor) { final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, From 1e0333c8fc3412aaf610bd22558a3f80fccf2ba6 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 23 Jun 2020 15:23:08 +0800 Subject: [PATCH 03/44] Update params Update function with no params and add plugin.xml --- jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml | 1 + .../src/com/microsoft/jdtls/ext/core/CommandHandler.java | 2 +- .../src/com/microsoft/jdtls/ext/core/ProjectCommand.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml index 5148ec64..94c1cb80 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml @@ -7,6 +7,7 @@ + arguments, IProgress case "java.resolvePath": return PackageCommand.resolvePath(arguments, monitor); case "java.project.getMainMethod": - return ProjectCommand.getMainMethod(arguments, monitor); + return ProjectCommand.getMainMethod(monitor); default: break; } diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index 428936fd..dd4eeb5e 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -97,7 +97,7 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } - public static List getMainMethod(List arguments, IProgressMonitor monitor) { + public static List getMainMethod(IProgressMonitor monitor) { final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, From afea0b1dbb6d04f73ad1319e689df0e3812af5de Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 24 Jun 2020 12:19:10 +0800 Subject: [PATCH 04/44] Update ProjectCommand.java --- .../jdtls/ext/core/ProjectCommand.java | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index dd4eeb5e..b3b3b926 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -24,6 +24,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.IMethod; @@ -40,16 +41,26 @@ import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; -import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; import org.eclipse.jdt.ls.core.internal.managers.UpdateClasspathJob; import org.eclipse.jdt.ls.core.internal.preferences.Preferences.ReferencedLibraries; import com.microsoft.jdtls.ext.core.model.NodeKind; import com.microsoft.jdtls.ext.core.model.PackageNode; - public final class ProjectCommand { + public static class MainClass { + + public String name; + + public String path; + + public MainClass(String name, String path) { + this.name = name; + this.path = path; + } + } + public static List listProjects(List arguments, IProgressMonitor monitor) { String workspaceUri = (String) arguments.get(0); IPath workspacePath = ResourceUtils.canonicalFilePathFromURI(workspaceUri); @@ -97,11 +108,11 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } - public static List getMainMethod(IProgressMonitor monitor) { - final List res = new ArrayList<>(); + public static List getMainMethod(IProgressMonitor monitor) { + final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH| SearchPattern.R_CASE_SENSITIVE); + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); SearchRequestor requestor = new SearchRequestor() { @Override public void acceptSearchMatch(SearchMatch match) { @@ -111,29 +122,30 @@ public void acceptSearchMatch(SearchMatch match) { try { if (method.isMainMethod()) { IResource resource = method.getResource(); - if (resource != null) { - IProject project = resource.getProject(); - if (project != null) { - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - IJavaProject javaProject = getJavaProject(project); - if (javaProject != null) { - String moduleName = getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } - } - String projectName = ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getName()) ? null : project.getName(); - String filePath = null; - if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } - } - res.add(new PackageNode(mainClass, filePath, NodeKind.FILE)); + if (resource == null) { + return; + } + IProject project = resource.getProject(); + if (project == null) { + return; + } + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + IJavaProject javaProject = method.getJavaProject(); + if (javaProject != null) { + String moduleName = getModuleName(javaProject); + if (moduleName != null) { + mainClass = moduleName + "/" + mainClass; + } + } + String filePath = null; + if (match.getResource() instanceof IFile) { + try { + filePath = match.getResource().getLocation().toOSString(); + } catch (Exception ex) { + // ignore } } + res.add(new MainClass(mainClass, filePath)); } } catch (JavaModelException e) { // ignore @@ -144,7 +156,7 @@ public void acceptSearchMatch(SearchMatch match) { SearchEngine searchEngine = new SearchEngine(); try { searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, - scope, requestor, null /* progress monitor */); + scope, requestor, new NullProgressMonitor() /* progress monitor */); } catch (Exception e) { // ignore } @@ -164,11 +176,4 @@ public static String getModuleName(IJavaProject project) { return module == null ? null : module.getElementName(); } - public static IJavaProject getJavaProject(IProject project) { - if (ProjectUtils.isJavaProject(project)) { - return JavaCore.create(project); - } - return null; - } - } From 86b2454f22fbe9b5cf01c1e8541153dc1117d69c Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 24 Jun 2020 13:01:29 +0800 Subject: [PATCH 05/44] Update ProjectCommand.java --- .../microsoft/jdtls/ext/core/ProjectCommand.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index b3b3b926..d2d2d2ea 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -49,13 +49,13 @@ public final class ProjectCommand { - public static class MainClass { + public static class MainClassInfo { public String name; public String path; - public MainClass(String name, String path) { + public MainClassInfo(String name, String path) { this.name = name; this.path = path; } @@ -108,8 +108,8 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } - public static List getMainMethod(IProgressMonitor monitor) { - final List res = new ArrayList<>(); + public static List getMainMethod(IProgressMonitor monitor) { + final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); @@ -125,10 +125,6 @@ public void acceptSearchMatch(SearchMatch match) { if (resource == null) { return; } - IProject project = resource.getProject(); - if (project == null) { - return; - } String mainClass = method.getDeclaringType().getFullyQualifiedName(); IJavaProject javaProject = method.getJavaProject(); if (javaProject != null) { @@ -145,7 +141,7 @@ public void acceptSearchMatch(SearchMatch match) { // ignore } } - res.add(new MainClass(mainClass, filePath)); + res.add(new MainClassInfo(mainClass, filePath)); } } catch (JavaModelException e) { // ignore @@ -156,7 +152,7 @@ public void acceptSearchMatch(SearchMatch match) { SearchEngine searchEngine = new SearchEngine(); try { searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, - scope, requestor, new NullProgressMonitor() /* progress monitor */); + scope, requestor, new NullProgressMonitor()); } catch (Exception e) { // ignore } From d461fb20aa724046af3acaa4ae0d32a0d0cb23d9 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 24 Jun 2020 13:47:15 +0800 Subject: [PATCH 06/44] Update ProjectCommand.java --- .../jdtls/ext/core/ProjectCommand.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index d2d2d2ea..ecb68ac2 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -125,26 +125,19 @@ public void acceptSearchMatch(SearchMatch match) { if (resource == null) { return; } - String mainClass = method.getDeclaringType().getFullyQualifiedName(); IJavaProject javaProject = method.getJavaProject(); - if (javaProject != null) { - String moduleName = getModuleName(javaProject); - if (moduleName != null) { - mainClass = moduleName + "/" + mainClass; - } + if (javaProject == null) { + return; } + String mainClass = method.getDeclaringType().getFullyQualifiedName(); String filePath = null; if (match.getResource() instanceof IFile) { - try { - filePath = match.getResource().getLocation().toOSString(); - } catch (Exception ex) { - // ignore - } + filePath = match.getResource().getLocation().toOSString(); } res.add(new MainClassInfo(mainClass, filePath)); } } catch (JavaModelException e) { - // ignore + return; } } } @@ -163,13 +156,12 @@ public static String getModuleName(IJavaProject project) { if (project == null || !JavaRuntime.isModularProject(project)) { return null; } - IModuleDescription module; try { - module = project.getModuleDescription(); + IModuleDescription module = project.getModuleDescription(); + return module == null ? null : module.getElementName(); } catch (CoreException e) { return null; } - return module == null ? null : module.getElementName(); } } From e3884b501a26a73e6ff9a1c244644f1f2972666e Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 24 Jun 2020 14:29:09 +0800 Subject: [PATCH 07/44] Update ProjectCommand.java --- .../jdtls/ext/core/ProjectCommand.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index ecb68ac2..9ef5ddaa 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -117,28 +117,22 @@ public static List getMainMethod(IProgressMonitor monitor) { @Override public void acceptSearchMatch(SearchMatch match) { Object element = match.getElement(); - if (element instanceof IMethod) { - IMethod method = (IMethod) element; - try { - if (method.isMainMethod()) { - IResource resource = method.getResource(); - if (resource == null) { - return; - } - IJavaProject javaProject = method.getJavaProject(); - if (javaProject == null) { - return; - } - String mainClass = method.getDeclaringType().getFullyQualifiedName(); - String filePath = null; - if (match.getResource() instanceof IFile) { - filePath = match.getResource().getLocation().toOSString(); - } - res.add(new MainClassInfo(mainClass, filePath)); - } - } catch (JavaModelException e) { + if (!(element instanceof IMethod)) { + return; + } + IMethod method = (IMethod) element; + try { + if (!method.isMainMethod() || method.getResource() == null || method.getJavaProject() == null) { return; } + String mainClass = method.getDeclaringType().getFullyQualifiedName(); + String filePath = ""; + if (match.getResource() instanceof IFile) { + filePath = match.getResource().getLocation().toOSString(); + } + res.add(new MainClassInfo(mainClass, filePath)); + } catch (JavaModelException e) { + // ignore } } }; @@ -146,7 +140,7 @@ public void acceptSearchMatch(SearchMatch match) { try { searchEngine.search(pattern, new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()}, scope, requestor, new NullProgressMonitor()); - } catch (Exception e) { + } catch (CoreException e) { // ignore } return res; From 9f819caa15e1f8908a33c9079c23553fec5487ea Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 16 Jul 2020 11:16:45 +0800 Subject: [PATCH 08/44] Export jar Support export jar feature. --- .../META-INF/MANIFEST.MF | 1 + .../com.microsoft.jdtls.ext.core/plugin.xml | 1 + .../jdtls/ext/core/CommandHandler.java | 2 + .../jdtls/ext/core/ProjectCommand.java | 88 ++++- .../internal/jarpackager/JarPackageUtil.java | 356 ++++++++++++++++++ jdtls.ext/target.target | 46 ++- package.json | 28 +- package.nls.json | 1 + package.nls.zh.json | 1 + src/anchor.ts | 10 + src/commands.ts | 50 +++ src/java/jdtls.ts | 9 + src/languageServerPlugin.ts | 106 ++++++ src/logger.ts | 61 +++ src/utility.ts | 74 +++- src/views/build.ts | 129 +++++++ src/views/dependencyDataProvider.ts | 3 + src/views/elementPickNode.ts | 15 + src/views/exportJarFile.ts | 327 ++++++++++++++++ src/views/projectNode.ts | 3 + src/views/projectPickNode.ts | 11 + src/views/quickPickNode.ts | 13 + src/views/workspaceNode.ts | 3 + 23 files changed, 1307 insertions(+), 31 deletions(-) create mode 100644 jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java create mode 100644 src/anchor.ts create mode 100644 src/languageServerPlugin.ts create mode 100644 src/logger.ts create mode 100644 src/views/build.ts create mode 100644 src/views/elementPickNode.ts create mode 100644 src/views/exportJarFile.ts create mode 100644 src/views/projectPickNode.ts create mode 100644 src/views/quickPickNode.ts diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF b/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF index ce1f6777..53a40f6f 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Import-Package: org.eclipse.jdt.core, Require-Bundle: org.eclipse.core.runtime, org.eclipse.jdt.ls.core, org.eclipse.jdt.core, + org.eclipse.jdt.core.manipulation, org.eclipse.core.resources, org.eclipse.lsp4j.jsonrpc, org.eclipse.lsp4j, diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml index 94c1cb80..c36f3919 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml @@ -8,6 +8,7 @@ + arguments, IProgress return PackageCommand.resolvePath(arguments, monitor); case "java.project.getMainMethod": return ProjectCommand.getMainMethod(monitor); + case "java.project.exportJar": + return ProjectCommand.exportJar(arguments, monitor); default: break; } diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index 9ef5ddaa..07bbd208 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -11,28 +11,39 @@ package com.microsoft.jdtls.ext.core; +import java.io.File; +import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Objects; - +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipFile; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.core.search.SearchMatch; @@ -46,6 +57,12 @@ import com.microsoft.jdtls.ext.core.model.NodeKind; import com.microsoft.jdtls.ext.core.model.PackageNode; +import org.eclipse.lsp4j.jsonrpc.json.adapters.CollectionTypeAdapter; +import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter; + +import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.write; +import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeArchive; + public final class ProjectCommand { @@ -61,6 +78,11 @@ public MainClassInfo(String name, String path) { } } + private static final Gson gson = new GsonBuilder() + .registerTypeAdapterFactory(new CollectionTypeAdapter.Factory()) + .registerTypeAdapterFactory(new EnumTypeAdapter.Factory()) + .create(); + public static List listProjects(List arguments, IProgressMonitor monitor) { String workspaceUri = (String) arguments.get(0); IPath workspacePath = ResourceUtils.canonicalFilePathFromURI(workspaceUri); @@ -108,7 +130,55 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { return fileName + "_" + Integer.toHexString(workspacePath.toPortableString().hashCode()); } - public static List getMainMethod(IProgressMonitor monitor) { + public static boolean exportJar(List arguments, IProgressMonitor monitor) { + if(arguments.size() < 3) { + return false; + } + String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class); + List classpaths = gson.fromJson(gson.toJson(arguments.get(1)), new TypeToken>(){}.getType()); + String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class); + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + if(mainMethod.length() > 0) { + manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,mainMethod); + } + try (JarOutputStream target = new JarOutputStream(new FileOutputStream(destination), manifest)){ + Set fDirectories = new HashSet<>(); + for(String classpath : classpaths){ + if (classpath != null){ + if(classpath.endsWith(".jar")){ + ZipFile zip = new ZipFile(classpath); + writeArchive(zip, true, true, target, fDirectories,monitor); + } + else { + File folder = new File(classpath); + recursiveFolder(folder, target, fDirectories, folder.getAbsolutePath().length() + 1); + } + } + } + } catch (Exception e){ + return false; + } + return true; + } + + private static void recursiveFolder(File folder, JarOutputStream fJarOutputStream, Set fDirectories, int len){ + File[] files = folder.listFiles(); + for(File file : files){ + if(file.isDirectory()) { + recursiveFolder(file, fJarOutputStream, fDirectories, len); + } else if(file.isFile()) { + try { + write(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories); + } + catch (Exception e){ + // do nothing + } + } + } + } + + public static List getMainMethod(IProgressMonitor monitor) throws Exception{ final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java new file mode 100644 index 00000000..1a0d40f9 --- /dev/null +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java @@ -0,0 +1,356 @@ +package org.eclipse.jdt.internal.jarpackager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.eclipse.core.runtime.*; +import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin; +import org.eclipse.jdt.internal.ui.IJavaStatusConstants; + +public class JarPackageUtil { + + /** + * Write the given entry describing the given content to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param entry the entry to write + * @param content the content to write + * @param fJarOutputStream the destination JarOutputStream + * + * @throws IOException If an I/O error occurred + * + * @since 1.14 + * + */ + public static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { + byte[] readBuffer= new byte[4096]; + try { + fJarOutputStream.putNextEntry(entry); + int count; + while ((count= content.read(readBuffer, 0, readBuffer.length)) != -1) + fJarOutputStream.write(readBuffer, 0, count); + } finally { + if (content != null) + content.close(); + + /* + * Commented out because some JREs throw an NPE if a stream + * is closed twice. This works because + * a) putNextEntry closes the previous entry + * b) closing the stream closes the last entry + */ + } + } + + /** + * Write the contents of the given zipfile to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder + * + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param progressMonitor the progressMonitor + * + * @return the MultiStatus saving the warnings during the process + * + * @since 1.14 + * + */ + public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntriesIncluded, + boolean isCompressed, JarOutputStream fJarOutputStream, + Set fDirectories, IProgressMonitor progressMonitor) { + MultiStatus fStatus = new MultiStatus(JavaManipulationPlugin.getPluginId(), IStatus.OK, ""); //$NON-NLS-1$ + Enumeration jarEntriesEnum= zipFile.entries(); + File zipFile1 = new File(zipFile.getName()); + try { + String zipFileCanonical = zipFile1.getCanonicalPath(); + + while (jarEntriesEnum.hasMoreElements()) { + ZipEntry zipEntry= jarEntriesEnum.nextElement(); + if (!zipEntry.isDirectory()) { + String entryName= zipEntry.getName(); + File zipEntryFile = new File(zipFile1, entryName); + String zipEntryCanonical = zipEntryFile.getCanonicalPath(); + if (zipEntryCanonical.startsWith(zipFileCanonical + File.separator)) { + addFile(entryName, zipEntry, zipFile, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories, fStatus); + } + else { + addWarning("Invalid path" + entryName, null, fStatus); //$NON-NLS-1$ + } + } + progressMonitor.worked(1); + if (progressMonitor.isCanceled()) + throw new OperationCanceledException(); + } + } catch (IOException e) { + addWarning("ZipFile error" + zipFile.getName(), null, fStatus); //$NON-NLS-1$ + e.printStackTrace(); + } + return fStatus; + } + + /** + * Write the entry to the destinationPath of the given JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder + * + * @param destinationPath the destinationPath in the jar file + * @param jarEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param fStatus the MultiStatus saving the warnings during the process + * + * @since 1.14 + * + */ + private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile zipFile, + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus){ + // Handle META-INF/MANIFEST.MF + if (destinationPath.equalsIgnoreCase("META-INF/MANIFEST.MF") //$NON-NLS-1$ + || (destinationPath.startsWith("META-INF/") && destinationPath.endsWith(".SF"))) { //$NON-NLS-1$//$NON-NLS-2$ + return; + } + try { + addZipEntry(jarEntry, zipFile, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); + } catch (IOException ex) { + if (ex instanceof ZipException && ex.getMessage() != null && ex.getMessage().startsWith("duplicate entry:")) {//$NON-NLS-1$ + // ignore duplicates in META-INF (*.SF, *.RSA) + if (!destinationPath.startsWith("META-INF/")) { //$NON-NLS-1$ + addWarning(ex.getMessage(), ex, fStatus); + } + } //else + //addWarning(Messages.format(JarPackagerMessagesCore.FatJarBuilder_error_readingArchiveFile, new Object[] { BasicElementLabels.getResourceName(zipFile.getName()), ex.getLocalizedMessage() }), ex, fStatus); + } + } + + /** + * Write the entry to the destinationPath of the given JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param zipEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException If an I/O error occurred + * + * @since 1.14 + * + */ + public static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + if (areDirectoryEntriesIncluded) + addDirectories(path, fJarOutputStream, fDirectories); + + JarEntry newEntry= new JarEntry(path.replace(File.separatorChar, '/')); + + if (isCompressed) + newEntry.setMethod(ZipEntry.DEFLATED); + // Entry is filled automatically. + else { + newEntry.setMethod(ZipEntry.STORED); + newEntry.setSize(zipEntry.getSize()); + newEntry.setCrc(zipEntry.getCrc()); + } + + long lastModified= System.currentTimeMillis(); + + // Set modification time + newEntry.setTime(lastModified); + + addEntry(newEntry, zipFile.getInputStream(zipEntry), fJarOutputStream); + } + + /** + * Creates the directory entries for the given path and writes it to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param destPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + */ + public static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + String path= destPath.replace(File.separatorChar, '/'); + int lastSlash= path.lastIndexOf('/'); + List directories= new ArrayList<>(2); + while (lastSlash != -1) { + path= path.substring(0, lastSlash + 1); + if (!fDirectories.add(path)) + break; + + JarEntry newEntry= new JarEntry(path); + newEntry.setMethod(ZipEntry.STORED); + newEntry.setSize(0); + newEntry.setCrc(0); + newEntry.setTime(System.currentTimeMillis()); + directories.add(newEntry); + + lastSlash= path.lastIndexOf('/', lastSlash - 1); + } + + for (int i= directories.size() - 1; i >= 0; --i) { + fJarOutputStream.putNextEntry(directories.get(i)); + } + } + + /** + * Write the single file to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param file the file to write + * @param destinationPath the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws CoreException if an error has occurred + * + * @since 1.14 + * + */ + public static void write(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, + boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories) throws CoreException { + try { + addFile(file, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); + } catch (IOException ex) { + // Ensure full path is visible + //String message= null; + //IPath path= new Path(file.getAbsolutePath()); + /*if (ex.getLocalizedMessage() != null) + message= Messages.format(JarPackagerMessagesCore.JarWriter_writeProblemWithMessage, + new Object[] { BasicElementLabels.getPathLabel(path, false), ex.getLocalizedMessage() }); + else + message= Messages.format(JarPackagerMessagesCore.JarWriter_writeProblem, BasicElementLabels.getPathLabel(path, false)); + if (message == null) + message= ""; //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, JavaManipulationPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, ex));*/ + } + } + + /** + * Add the single file to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param file the file to write + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + * + */ + private static void addFile(File file, IPath path, boolean areDirectoryEntriesIncluded, + boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + if (areDirectoryEntriesIncluded) + addDirectories(path, fJarOutputStream, fDirectories); + + JarEntry newEntry= new JarEntry(path.toString().replace(File.separatorChar, '/')); + + if (isCompressed) + newEntry.setMethod(ZipEntry.DEFLATED); + // Entry is filled automatically. + else { + newEntry.setMethod(ZipEntry.STORED); + calculateCrcAndSize(newEntry, new FileInputStream(file), new byte[4096]); + } + + newEntry.setTime(file.lastModified()); + addEntry(newEntry, new FileInputStream(file), fJarOutputStream); + } + + /** + * Creates the directory entries for the given path and writes it to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param destinationPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + */ + public static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + addDirectories(destinationPath.toString(), fJarOutputStream, fDirectories); + } + + /** + * Calculates the crc and size of the resource and updates the entry. + * Extracted from org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil + * + * @param entry the jar entry to update + * @param stream the input stream + * @param buffer a shared buffer to store temporary data + * + * @throws IOException if an input/output error occurs + * + * @since 1.14 + */ + public static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { + int size= 0; + final CRC32 crc= new CRC32(); + int count; + try { + while ((count= stream.read(buffer, 0, buffer.length)) != -1) { + crc.update(buffer, 0, count); + size+= count; + } + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException exception) { + // Do nothing + } + } + } + entry.setSize(size); + entry.setCrc(crc.getValue()); + } + + /** + * add a warning message into the MultiStatus. + * + * @param message the message to add + * @param error the reason of the message + * @param fStatus the MultiStatus to write + * + * @since 1.14 + */ + protected final static void addWarning(String message, Throwable error, MultiStatus fStatus) { + fStatus.add(new Status(IStatus.WARNING, JavaManipulationPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, error)); + } + +} + + diff --git a/jdtls.ext/target.target b/jdtls.ext/target.target index fd2606f8..3c3a5aaa 100644 --- a/jdtls.ext/target.target +++ b/jdtls.ext/target.target @@ -1,10 +1,28 @@ - + - - + + + + + + + + + + + + + + + + + + + + @@ -12,11 +30,10 @@ - - - - - + + + + @@ -24,16 +41,13 @@ - + - - - - - - - + + + + diff --git a/package.json b/package.json index 725428aa..dd4feeb6 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,12 @@ "title": "%contributes.commands.java.view.package.revealFileInOS%", "category": "Java" }, + { + "command": "java.view.package.exportJar", + "title": "%contributes.commands.java.view.package.exportJar%", + "category": "Java", + "icon": "$(arrow-down)" + }, { "command": "java.view.package.copyFilePath", "title": "%contributes.commands.java.view.package.copyFilePath%", @@ -176,6 +182,10 @@ "command": "java.view.package.revealFileInOS", "when": "never" }, + { + "command": "java.view.package.exportJar", + "when": "java:serverMode != LightWeight" + }, { "command": "java.view.package.copyFilePath", "when": "never" @@ -202,6 +212,11 @@ } ], "view/title": [ + { + "command": "java.view.package.exportJar", + "when": "view == javaProjectExplorer && java:serverMode!= LightWeight", + "group": "navigation@3" + }, { "command": "java.view.package.refresh", "when": "view == javaProjectExplorer && java:serverMode!= LightWeight", @@ -246,23 +261,28 @@ }, { "command": "java.project.addLibraries", - "when": "view == javaProjectExplorer && viewItem =~ /java:container\/referenced-libraries$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:container/referenced-libraries$/", "group": "inline@0" }, { "command": "java.project.removeLibrary", - "when": "view == javaProjectExplorer && viewItem =~ /java:jar\/referenced-libraries\\+uri$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:jar/referenced-libraries\\+uri$/", "group": "inline" }, { "command": "java.project.refreshLibraries", - "when": "view == javaProjectExplorer && viewItem =~ /java:container\/referenced-libraries$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:container/referenced-libraries$/", "group": "inline@1" }, { "command": "java.project.maven.addDependency", - "when": "view == javaProjectExplorer && mavenEnabled && viewItem =~ /container\/maven-dependencies/", + "when": "view == javaProjectExplorer && mavenEnabled && viewItem =~ /container/maven-dependencies/", "group": "inline@0" + }, + { + "command": "java.view.package.exportJar", + "when": "view == javaProjectExplorer && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", + "group": "inline" } ] }, diff --git a/package.nls.json b/package.nls.json index 78182ec4..8c5fbdc1 100644 --- a/package.nls.json +++ b/package.nls.json @@ -11,6 +11,7 @@ "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.exportJar": "Export Jar", "contributes.commands.java.view.package.copyFilePath": "Copy Path", "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "configuration.java.dependency.title": "Java Dependency Configuration", diff --git a/package.nls.zh.json b/package.nls.zh.json index 81756f70..bb5a7693 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -11,6 +11,7 @@ "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.exportJar": "导出到Jar文件", "contributes.commands.java.view.package.copyFilePath": "复制路径", "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", diff --git a/src/anchor.ts b/src/anchor.ts new file mode 100644 index 00000000..145c3d5d --- /dev/null +++ b/src/anchor.ts @@ -0,0 +1,10 @@ + +export const LS_FAILS_TO_START = "java-language-support-extension-fails-to-start"; +export const FAILED_TO_RESOLVE_CLASSPATH = "failed-to-resolve-classpath"; +export const REQUEST_TYPE_NOT_SUPPORTED = "request-type-xyz-is-not-supported-only-launch-and-attach-are-supported"; +export const FAILED_TO_COMPLETE_HCR = "failed-to-complete-hot-code-replace"; +export const ATTACH_CONFIG_ERROR = "please-specify-the-host-name-and-the-port-of-the-remote-debuggee-in-the-launchjson"; +export const EVALUATE_ON_RUNNING_THREAD = "failed-to-evaluate-reason-cannot-evaluate-because-the-thread-is-resumed"; +export const CANNOT_FIND_MAIN_CLASS = "cannot-find-a-class-with-the-main-method"; +export const BUILD_FAILED = "build-failed-do-you-want-to-continue"; +export const UNSUPPORTED_CLASS_VERSION_ERROR = "program-throws-unsupportedclassversionerror"; diff --git a/src/commands.ts b/src/commands.ts index e68846c5..645253fa 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -4,6 +4,8 @@ /** * Commonly used commands */ +import { commands } from "vscode"; +import { getJavaExtension, JavaExtensionNotEnabledError } from "./utility"; export namespace Commands { /** * Execute Workspace Command @@ -30,6 +32,7 @@ export namespace Commands { export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; + export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar"; export const JAVA_PROJECT_CREATE = "java.project.create"; export const JAVA_PROJECT_ADD_LIBRARIES = "java.project.addLibraries"; @@ -57,5 +60,52 @@ export namespace Commands { export const JAVA_RESOLVEPATH = "java.resolvePath"; + export const JAVA_PROJECT_GETMAINMETHOD = "java.project.getMainMethod"; + + export const JAVA_PROJECT_EXPORTJAR = "java.project.exportJar"; export const VSCODE_OPEN_FOLDER = "vscode.openFolder"; } + +export const JAVA_BUILD_WORKSPACE = "java.workspace.compile"; + +export const JAVA_EXECUTE_WORKSPACE_COMMAND = "java.execute.workspaceCommand"; + +export const JAVA_RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod"; + +export const JAVA_START_DEBUGSESSION = "vscode.java.startDebugSession"; + +export const JAVA_RESOLVE_CLASSPATH = "vscode.java.resolveClasspath"; + +export const JAVA_RESOLVE_MAINCLASS = "vscode.java.resolveMainClass"; + +export const JAVA_VALIDATE_LAUNCHCONFIG = "vscode.java.validateLaunchConfig"; + +export const JAVA_INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandLength"; + +export const JAVA_CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; + +export const JAVA_RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; + +export const JAVA_RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; + +export const JAVA_IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; + +export const JAVA_RESOLVE_JAVAEXECUTABLE = "vscode.java.resolveJavaExecutable"; + +export const JAVA_FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; + +export function executeJavaLanguageServerCommand(...rest) { + return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest); +} + +export async function executeJavaExtensionCommand(commandName: string, ...rest) { + // TODO: need to handle error and trace telemetry + const javaExtension = getJavaExtension(); + if (!javaExtension) { + throw new JavaExtensionNotEnabledError(`Cannot execute command ${commandName}, VS Code Java Extension is not enabled.`); + } + if (!javaExtension.isActive) { + await javaExtension.activate(); + } + return commands.executeCommand(commandName, ...rest); +} diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index a1b60955..3ed23af4 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,6 +3,7 @@ import { commands } from "vscode"; import { Commands } from "../commands"; +import { MainMethodInfo } from "../views/exportJarFile"; import { INodeData } from "./nodeData"; export namespace Jdtls { @@ -21,4 +22,12 @@ export namespace Jdtls { export function resolvePath(params): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params); } + + export function getMainMethod(): Thenable { + return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINMETHOD); + } + + export function exportJar(mainMethod, elements, destination): Thenable { + return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_EXPORTJAR, mainMethod, elements, destination); + } } diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts new file mode 100644 index 00000000..1ba21f14 --- /dev/null +++ b/src/languageServerPlugin.ts @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as vscode from "vscode"; + +import * as commands from "./commands"; + +export enum CompileWorkspaceStatus { + FAILED = 0, + SUCCEED = 1, + WITHERROR = 2, + CANCELLED = 3, +} + +export interface IMainClassOption { + readonly mainClass: string; + readonly projectName?: string; + readonly filePath?: string; +} + +export interface IMainMethod extends IMainClassOption { + range: vscode.Range; +} + +export interface IValidationResult { + readonly isValid: boolean; + readonly message?: string; +} + +export interface ILaunchValidationResponse { + readonly mainClass: IValidationResult; + readonly projectName: IValidationResult; + readonly proposals?: IMainClassOption[]; +} + +export async function resolveMainMethod(uri: vscode.Uri): Promise { + return await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString()); +} + +export function startDebugSession() { + return commands.executeJavaLanguageServerCommand(commands.JAVA_START_DEBUGSESSION); +} + +export function resolveClasspath(mainClass, projectName) { + return commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_CLASSPATH, mainClass, projectName); +} + +export function resolveMainClass(workspaceUri: vscode.Uri): Promise { + if (workspaceUri) { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINCLASS, workspaceUri.toString()); + } + return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINCLASS); +} + +export function validateLaunchConfig(workspaceUri: vscode.Uri, mainClass: string, projectName: string, containsExternalClasspaths: boolean): + Promise { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_VALIDATE_LAUNCHCONFIG, + workspaceUri ? workspaceUri.toString() : undefined, mainClass, projectName, containsExternalClasspaths); +} + +export function inferLaunchCommandLength(config: vscode.DebugConfiguration): Promise { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_INFER_LAUNCH_COMMAND_LENGTH, JSON.stringify(config)); +} + +export function checkProjectSettings(className: string, projectName: string, inheritedOptions: boolean, expectedOptions: {[key: string]: string}): +Promise { + return >commands.executeJavaLanguageServerCommand( + commands.JAVA_CHECK_PROJECT_SETTINGS, JSON.stringify({ + className, + projectName, + inheritedOptions, + expectedOptions, + })); +} + +const COMPILER_PB_ENABLE_PREVIEW_FEATURES: string = "org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures"; +export async function detectPreviewFlag(className: string, projectName: string): Promise { + const expectedOptions = { + [COMPILER_PB_ENABLE_PREVIEW_FEATURES]: "enabled", + }; + return checkProjectSettings(className, projectName, true, expectedOptions); +} + +export function resolveElementAtSelection(uri: string, line: number, character: number): Promise { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_ELEMENT_AT_SELECTION, uri, line, character); +} + +export function resolveBuildFiles(): Promise { + return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_BUILD_FILES); +} + +export async function isOnClasspath(uri: string): Promise { + try { + return await commands.executeJavaExtensionCommand(commands.JAVA_IS_ON_CLASSPATH, uri); + } catch (error) { + return true; + } +} + +export function resolveJavaExecutable(mainClass, projectName) { + return commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_JAVAEXECUTABLE, mainClass, projectName); +} + +export function fetchPlatformSettings(): any { + return commands.executeJavaLanguageServerCommand(commands.JAVA_FETCH_PLATFORM_SETTINGS); +} diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 00000000..f765b4b4 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as fs from "fs"; +import * as vscode from "vscode"; +import TelemetryReporter from "vscode-extension-telemetry"; + +export enum Type { + EXCEPTION = "exception", + USAGEDATA = "usageData", + USAGEERROR = "usageError", + ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category. +} + +const SENSITIVE_PROPS = ["message", "stacktrace", "detailmessage"]; + +class Logger implements vscode.Disposable { + private reporter: TelemetryReporter = null; + + public initialize(context: vscode.ExtensionContext, firstParty?: boolean): void { + if (this.reporter) { + return; + } + + const extensionPackage = JSON.parse(fs.readFileSync(context.asAbsolutePath("./package.json"), "utf-8")); + if (extensionPackage) { + const packageInfo = { + name: extensionPackage.name, + version: extensionPackage.version, + aiKey: extensionPackage.aiKey, + }; + if (packageInfo.aiKey) { + this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey, firstParty); + } + } + } + + public log(type: Type, properties?: { [key: string]: string; }, measures?: { [key: string]: number; }): void { + if (!this.reporter) { + return; + } + + if (type === Type.EXCEPTION || type === Type.USAGEERROR) { + this.reporter.sendTelemetryErrorEvent(type, properties, measures, SENSITIVE_PROPS); + } else { + this.reporter.sendTelemetryEvent(type, properties, measures); + } + } + + public logMessage(type: Type, message: string): void { + this.log(type, { message }); + } + + public dispose() { + if (this.reporter) { + this.reporter.dispose(); + } + } +} + +export const logger = new Logger(); diff --git a/src/utility.ts b/src/utility.ts index 60973a31..50787405 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,7 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { window, workspace, WorkspaceFolder } from "vscode"; +import { commands, Extension, extensions, ProgressLocation, Uri, window, workspace, WorkspaceFolder } from "vscode"; +import { setUserError } from "vscode-extension-telemetry-wrapper"; +import { logger, Type } from "./logger"; +const JAVA_EXTENSION_ID = "redhat.java"; +const TROUBLESHOOTING_LINK = "https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md"; export class Utility { @@ -13,9 +17,75 @@ export class Utility { return workspace.workspaceFolders[0]; } if (window.activeTextEditor) { - const activeWorkspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri); + const activeWorkspaceFolder: WorkspaceFolder | undefined = + workspace.getWorkspaceFolder(window.activeTextEditor.document.uri); return activeWorkspaceFolder; } return undefined; } + +} + +interface IProperties { + [key: string]: string; +} + +interface ILoggingMessage { + message: string; + type?: Type; + details?: IProperties; +} + +interface ITroubleshootingMessage extends ILoggingMessage { + anchor?: string; +} + +export class UserError extends Error { + public context: ITroubleshootingMessage; + + constructor(context: ITroubleshootingMessage) { + super(context.message); + this.context = context; + setUserError(this); + } +} + +export function getJavaExtension(): Extension { + return extensions.getExtension(JAVA_EXTENSION_ID); +} + +export class JavaExtensionNotEnabledError extends Error { + constructor(message) { + super(message); + setUserError(this); + } +} + +export async function guideToInstallJavaExtension() { + const MESSAGE = "Language Support for Java is required. Please install and enable it."; + const INSTALL = "Install"; + const choice = await window.showWarningMessage(MESSAGE, INSTALL); + if (choice === INSTALL) { + await installJavaExtension(); + } +} + +async function installJavaExtension() { + await window.withProgress({ location: ProgressLocation.Notification }, async (p) => { + p.report({ message: "Installing Language Support for Java ..." }); + await commands.executeCommand("workbench.extensions.installExtension", JAVA_EXTENSION_ID); + }); + const RELOAD = "Reload Window"; + const choice = await window.showInformationMessage("Please reload window to activate Language Support for Java.", RELOAD); + if (choice === RELOAD) { + await commands.executeCommand("workbench.action.reloadWindow"); + } +} + +export function openTroubleshootingPage(message: string, anchor: string) { + commands.executeCommand("vscode.open", Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); + logger.log(Type.USAGEDATA, { + troubleshooting: "yes", + troubleshootingMessage: message, + }); } diff --git a/src/views/build.ts b/src/views/build.ts new file mode 100644 index 00000000..51eaf9f9 --- /dev/null +++ b/src/views/build.ts @@ -0,0 +1,129 @@ +import * as path from "path"; +import * as vscode from "vscode"; +import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; + +import * as anchor from "../anchor"; +import * as commands from "../commands"; +import * as lsPlugin from "../languageServerPlugin"; +import * as utility from "../utility"; + +export async function buildWorkspace(): Promise { + const buildResult = await instrumentOperation("build", async (operationId: string) => { + let error; + try { + await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false); + } catch (err) { + error = err; + } + + return { + error, + operationId, + }; + })(); + + if (buildResult.error) { + return handleBuildFailure(buildResult.operationId, buildResult.error); + } + return true; +} + +async function handleBuildFailure(operationId: string, err: any): Promise { + if (err instanceof utility.JavaExtensionNotEnabledError) { + utility.guideToInstallJavaExtension(); + return false; + } + + const error: Error = new utility.UserError({ + message: "Build failed", + }); + setErrorCode(error, Number(err)); + sendOperationError(operationId, "build", error); + if (err === lsPlugin.CompileWorkspaceStatus.WITHERROR || err === lsPlugin.CompileWorkspaceStatus.FAILED) { + if (checkErrorsReportedByJavaExtension()) { + vscode.commands.executeCommand("workbench.actions.view.problems"); + } + + const ans = await vscode.window.showErrorMessage("Build failed, do you want to continue?", + "Proceed", "Fix...", "Cancel"); + sendInfo(operationId, { + operationName: "build", + choiceForBuildError: ans || "esc", + }); + if (ans === "Proceed") { + return true; + } else if (ans === "Fix...") { + showFixSuggestions(operationId); + } + + return false; + } + + return false; +} + +function checkErrorsReportedByJavaExtension(): boolean { + const problems = vscode.languages.getDiagnostics() || []; + for (const problem of problems) { + const fileName = path.basename(problem[0].fsPath || ""); + if (fileName.endsWith(".java") || fileName === "pom.xml" || fileName.endsWith(".gradle")) { + if (problem[1].filter((diagnostic) => diagnostic.severity === vscode.DiagnosticSeverity.Error).length) { + return true; + } + } + } + + return false; +} + +async function showFixSuggestions(operationId: string) { + let buildFiles = []; + try { + buildFiles = await lsPlugin.resolveBuildFiles(); + } catch (error) { + // do nothing + } + + const pickitems = []; + pickitems.push({ + label: "Clean workspace cache", + detail: "Clean the stale workspace and reload the window", + }); + if (buildFiles.length) { + pickitems.push({ + label: "Update project configuration", + detail: "Force the language server to update the project configuration/classpath", + }); + } + pickitems.push({ + label: "Open log file", + detail: "Open log file to view more details for the build errors", + }); + pickitems.push({ + label: "Troubleshooting guide", + detail: "Find more detail about the troubleshooting steps", + }); + + const ans = await vscode.window.showQuickPick(pickitems, { + placeHolder: "Please fix the errors in PROBLEMS first, then try the fix suggestions below.", + }); + sendInfo(operationId, { + operationName: "build", + choiceForBuildFix: ans ? ans.label : "esc", + }); + if (!ans) { + return; + } + + if (ans.label === "Clean workspace cache") { + vscode.commands.executeCommand("java.clean.workspace"); + } else if (ans.label === "Update project configuration") { + for (const buildFile of buildFiles) { + await vscode.commands.executeCommand("java.projectConfiguration.update", vscode.Uri.parse(buildFile)); + } + } else if (ans.label === "Open log file") { + vscode.commands.executeCommand("java.open.serverLog"); + } else if (ans.label === "Troubleshooting guide") { + utility.openTroubleshootingPage("Build failed", anchor.BUILD_FAILED); + } +} diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index 449200cd..d0592933 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -14,6 +14,7 @@ import { INodeData, NodeKind } from "../java/nodeData"; import { Settings } from "../settings"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; +import { ExportJarFile } from "./exportJarFile"; import { LightWeightNode } from "./lightWeightNode"; import { ProjectNode } from "./projectNode"; import { WorkspaceNode } from "./workspaceNode"; @@ -30,6 +31,8 @@ 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_EXPORT_JAR, (node: INodeData) => + ExportJarFile.createJarFile(node))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => diff --git a/src/views/elementPickNode.ts b/src/views/elementPickNode.ts new file mode 100644 index 00000000..33b05b77 --- /dev/null +++ b/src/views/elementPickNode.ts @@ -0,0 +1,15 @@ +import { QuickPickNode } from "./quickPickNode"; + +export class ElementPickNode extends QuickPickNode { + + public uri: string; + public type: string; + public picked: boolean; + + constructor(_label: string, _description: string, _uri: string, _type: string, _picked: boolean) { + super(_label, _description); + this.uri = _uri; + this.type = _type; + this.picked = _picked; + } +} diff --git a/src/views/exportJarFile.ts b/src/views/exportJarFile.ts new file mode 100644 index 00000000..20fecc22 --- /dev/null +++ b/src/views/exportJarFile.ts @@ -0,0 +1,327 @@ +import { EOL } from "os"; +import { basename, extname, join } from "path"; +import { CancellationToken, commands, Extension, extensions, MessageItem, MessageOptions, + ProgressLocation, QuickInputButtons, Uri, window, workspace, WorkspaceFolder } from "vscode"; + +import { isStandardServerReady } from "../extension"; +import { Jdtls } from "../java/jdtls"; +import { INodeData } from "../java/nodeData"; + +import { buildWorkspace } from "./build"; +import { ElementPickNode } from "./elementPickNode"; +import { ProjectPickNode } from "./projectPickNode"; +import { QuickPickNode } from "./quickPickNode"; +import { WorkspaceNode } from "./workspaceNode"; + +const SOLVE_PROJECT = "solve project"; +const SOLVE_MAINMETHOD = "solve mainmethod"; +const GENERATE_JAR = "generate jar"; +const FINISH = "finish"; + +export class ExportJarFile { + + public static mainMethods: MainMethodInfo[]; + + public static async createJarFile(node?: INodeData) { + if (!isStandardServerReady()) { + return; + } + window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar", + cancellable: true, + }, (progress, token): Promise => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + reject(); + }); + progress.report({ increment: 10, message: "Building workspace..." }); + if (await buildWorkspace() === false) { + return reject(); + } + this.mainMethods = await Jdtls.getMainMethod(); + const pickSteps: string[] = []; + let step: string = SOLVE_PROJECT; + let rootNodes: INodeData[] = []; + let projectFolder: WorkspaceFolder; + let pickResult: string; + let outputFileName: string; + while (step !== FINISH) { + try { + switch (step) { + case SOLVE_PROJECT: { + projectFolder = await this.solveProject(progress, token, pickSteps, node); + rootNodes = await Jdtls.getProjects(projectFolder.uri.toString()); + step = SOLVE_MAINMETHOD; + break; + } + case SOLVE_MAINMETHOD: { + pickResult = await this.solveMainMethod(progress, token, pickSteps, projectFolder.uri.fsPath); + step = GENERATE_JAR; + break; + } + case GENERATE_JAR: { + outputFileName = await this.writingJar(progress, token, pickSteps, rootNodes, pickResult, projectFolder.uri.fsPath); + resolve(outputFileName); + step = FINISH; + break; + } + } + } catch (err) { + if (err === InputFlowAction.back) { + step = pickSteps.pop(); + continue; + } else { + return reject(); + } + } + } + }); + }).then((message) => { this.successMessage(message); }, () => {}); + } + + private static solveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { + return new Promise((resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } + const folders = workspace.workspaceFolders; + let projectFolder: WorkspaceFolder; + if (node instanceof WorkspaceNode) { + folders.forEach((folder) => { + if (folder.uri.toString() === node.uri) { + return resolve(folder); + } + }); + return reject(); + } + if (folders && folders.length) { + if (folders.length === 1) { + return resolve(folders[0]); + } + progress.report({ increment: 10, message: "Determining project..." }); + const pickNodes: ProjectPickNode[] = []; + for (const folder of folders) { + pickNodes.push(new ProjectPickNode(folder.name, folder.uri.fsPath, folder.uri.fsPath)); + } + const pickBox = window.createQuickPick(); + pickBox.items = pickNodes; + pickBox.title = "Export Jar - Determine project"; + pickBox.placeholder = "Select the project..."; + pickBox.ignoreFocusOut = true; + pickBox.onDidAccept(() => { + pickSteps.push(SOLVE_PROJECT); + folders.forEach((folder) => { + if (folder.uri.fsPath === pickBox.selectedItems[0].uri) { + projectFolder = folder; + } + }); + resolve(projectFolder); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + } else { + this.failMessage("No project found"); + return reject(); + } + }); + } + + private static writingJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], + description: string, outputPath: string): Promise { + return new Promise(async (resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } else if (rootNodes === undefined) { + this.failMessage("No module found in this project"); + return reject(); + } + progress.report({ increment: 10, message: "Solving classpaths..." }); + let outClassPaths: string[]; + try { + outClassPaths = await this.generateOutClassPath(pickSteps, rootNodes, outputPath); + } catch (e) { + return reject(e); + } + const outputFileName = join(outputPath, basename(outputPath) + ".jar"); + progress.report({ increment: 30, message: "Exporting jar..." }); + const exportResult = await Jdtls.exportJar(basename(description), outClassPaths, outputFileName); + if (exportResult === true) { + resolve(outputFileName); + } else { + reject(); + } + }); + } + + private static solveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { + return new Promise(async (resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } + progress.report({ increment: 10, message: "Getting main classes..." }); + if (this.mainMethods === undefined || this.mainMethods.length === 0) { + return resolve(""); + } + progress.report({ increment: 30, message: "Determining entry main class..." }); + const pickNodes: QuickPickNode[] = []; + for (const mainMethod of this.mainMethods) { + if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { + pickNodes.push(new QuickPickNode(this.getName(mainMethod), mainMethod.name)); + } + } + if (pickNodes.length === 0) { + return resolve(""); + } else { + const pickBox = window.createQuickPick(); + pickNodes.push(new QuickPickNode("No main class", "")); + pickBox.items = pickNodes; + pickBox.title = "Export Jar - Determine entry main class"; + pickBox.placeholder = "Select the entry main class..."; + pickBox.ignoreFocusOut = true; + pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + pickSteps.push(SOLVE_MAINMETHOD); + resolve(pickBox.selectedItems[0].description); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + } + }); + } + + private static failMessage(message: string) { + window.showInformationMessage(message, new Option(false), new Message("Done", true)); + } + + private static successMessage(outputFileName: string) { + const openInExplorer: Message = new Message("Reveal in File Explorer"); + window.showInformationMessage("Successfully exported jar to" + EOL + outputFileName, + new Option(false), openInExplorer, new Message("Done", true)).then((messageResult) => { + if (messageResult === openInExplorer) { + commands.executeCommand("revealFileInOS", Uri.file(outputFileName)); + } + }); + } + + private static async generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { + return new Promise(async (resolve, reject) => { + const extension: Extension | undefined = extensions.getExtension("redhat.java"); + const extensionApi: any = await extension?.activate(); + const outClassPaths: string[] = []; + const setUris: Set = new Set(); + const pickDependencies: ElementPickNode[] = []; + const pickedDependencies: ElementPickNode[] = []; + for (const rootNode of rootNodes) { + const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + this.generateDependList(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); + this.generateDependList(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); + const modulePathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + this.generateDependList(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); + this.generateDependList(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); + } + const pickBox = window.createQuickPick(); + pickDependencies.sort((node1, node2) => { + if (node1.description !== node2.description) { + return node1.description.localeCompare(node2.description); + } + if (node1.type !== node2.type) { + return node2.type.localeCompare(node1.type); + } + return node1.label.localeCompare(node2.label); + }); + pickBox.items = pickDependencies; + pickDependencies.forEach((pickDependency) => { + if (pickDependency.picked) { + pickedDependencies.push(pickDependency); + } + }); + pickBox.selectedItems = pickedDependencies; + pickBox.title = "Export Jar - Determine elements"; + pickBox.placeholder = "Select the elements..."; + pickBox.canSelectMany = true; + pickBox.ignoreFocusOut = true; + pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + pickBox.selectedItems.forEach((item) => { + outClassPaths.push(item.uri); + }); + resolve(outClassPaths); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + }); + } + + private static generateDependList(paths: string[], setUris: Set, pickDependencies: ElementPickNode[], + projectPath: string, isRuntime: boolean) { + paths.forEach((classpath: string) => { + const extName = extname(classpath); + const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); + const description = (isRuntime) ? "Runtime" : "Test"; + const type = (extName === ".jar") ? "external" : "internal"; + if (!setUris.has(classpath)) { + setUris.add(classpath); + pickDependencies.push(new ElementPickNode(baseName, description, classpath, type, isRuntime)); + } + }); + } + private static getName(data: MainMethodInfo) { + const point = data.name.lastIndexOf("."); + if (point === -1) { + return data.name; + } else { + return data.name.substring(point + 1); + } + } + +} + +class Option implements MessageOptions { + constructor(public modal?: boolean) { + } +} + +class Message implements MessageItem { + constructor(public title: string, public isCloseAffordance?: boolean) { + } +} + +class ClasspathResult { + public projectRoot: string; + public classpaths: string[]; + public modulepaths: string[]; +} + +export class MainMethodInfo { + public name: string; + public path: string; +} + +class InputFlowAction { + public static back = new InputFlowAction(); +} diff --git a/src/views/projectNode.ts b/src/views/projectNode.ts index 4511d133..3c309ea6 100644 --- a/src/views/projectNode.ts +++ b/src/views/projectNode.ts @@ -69,4 +69,7 @@ export class ProjectNode extends DataNode { protected get iconPath(): ThemeIcon { return new ThemeIcon("project"); } + protected get contextValue(): string { + return `project/${this.name}`; + } } diff --git a/src/views/projectPickNode.ts b/src/views/projectPickNode.ts new file mode 100644 index 00000000..c2df6324 --- /dev/null +++ b/src/views/projectPickNode.ts @@ -0,0 +1,11 @@ +import { QuickPickNode } from "./quickPickNode"; + +export class ProjectPickNode extends QuickPickNode { + + public uri: string; + + constructor(_label: string, _description: string, _uri: string) { + super(_label, _description); + this.uri = _uri; + } +} diff --git a/src/views/quickPickNode.ts b/src/views/quickPickNode.ts new file mode 100644 index 00000000..02b964e8 --- /dev/null +++ b/src/views/quickPickNode.ts @@ -0,0 +1,13 @@ +import { QuickPickItem } from "vscode"; + +export class QuickPickNode implements QuickPickItem { + + public label: string; + public description: string; + + constructor(_label: string, _description: string) { + this.label = _label; + this.description = _description; + } + +} diff --git a/src/views/workspaceNode.ts b/src/views/workspaceNode.ts index 78fd1085..a8826ce2 100644 --- a/src/views/workspaceNode.ts +++ b/src/views/workspaceNode.ts @@ -30,4 +30,7 @@ export class WorkspaceNode extends DataNode { protected get iconPath(): ThemeIcon { return new ThemeIcon("root-folder"); } + protected get contextValue(): string { + return `workspace/${this.name}`; + } } From 7c844e1ebce67b9f466674fb52f5b0fc234753e3 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 16 Jul 2020 11:25:35 +0800 Subject: [PATCH 09/44] Update pom.xml --- jdtls.ext/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdtls.ext/pom.xml b/jdtls.ext/pom.xml index 73d68ee8..1c09a851 100644 --- a/jdtls.ext/pom.xml +++ b/jdtls.ext/pom.xml @@ -81,9 +81,9 @@ - 201906 + 202009 p2 - http://download.eclipse.org/releases/2019-06/ + http://download.eclipse.org/releases/2020-09/ oss.sonatype.org From 9b8a61292a0c39d607c437bd7c797c054fa94684 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 16 Jul 2020 13:24:35 +0800 Subject: [PATCH 10/44] Update ProjectCommand.java --- .../src/com/microsoft/jdtls/ext/core/ProjectCommand.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index 07bbd208..a32e690b 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -63,7 +63,6 @@ import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.write; import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeArchive; - public final class ProjectCommand { public static class MainClassInfo { From 317c840f87418e0673d86ec6778d0c9e411fd62a Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Fri, 17 Jul 2020 17:36:52 +0800 Subject: [PATCH 11/44] Apply changes --- .../META-INF/MANIFEST.MF | 1 - .../jdtls/ext/core/ProjectCommand.java | 13 +- .../internal/jarpackager/JarPackageUtil.java | 180 ++++++++++-------- jdtls.ext/pom.xml | 4 +- jdtls.ext/target.target | 14 +- package.json | 8 +- package.nls.zh.json | 2 +- src/anchor.ts | 10 - src/commands.ts | 31 +-- src/languageServerPlugin.ts | 94 +-------- src/logger.ts | 2 +- src/utility.ts | 56 ++---- src/views/build.ts | 15 +- src/views/elementPickNode.ts | 15 -- src/views/exportJarFile.ts | 62 +++--- src/views/projectPickNode.ts | 11 -- src/views/quickPickNode.ts | 11 +- 17 files changed, 191 insertions(+), 338 deletions(-) delete mode 100644 src/anchor.ts delete mode 100644 src/views/elementPickNode.ts delete mode 100644 src/views/projectPickNode.ts diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF b/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF index 53a40f6f..ce1f6777 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/META-INF/MANIFEST.MF @@ -12,7 +12,6 @@ Import-Package: org.eclipse.jdt.core, Require-Bundle: org.eclipse.core.runtime, org.eclipse.jdt.ls.core, org.eclipse.jdt.core, - org.eclipse.jdt.core.manipulation, org.eclipse.core.resources, org.eclipse.lsp4j.jsonrpc, org.eclipse.lsp4j, diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index a32e690b..bb4c5eae 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -26,7 +26,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; @@ -60,7 +59,7 @@ import org.eclipse.lsp4j.jsonrpc.json.adapters.CollectionTypeAdapter; import org.eclipse.lsp4j.jsonrpc.json.adapters.EnumTypeAdapter; -import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.write; +import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeFile; import static org.eclipse.jdt.internal.jarpackager.JarPackageUtil.writeArchive; public final class ProjectCommand { @@ -134,7 +133,7 @@ public static boolean exportJar(List arguments, IProgressMonitor monitor return false; } String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class); - List classpaths = gson.fromJson(gson.toJson(arguments.get(1)), new TypeToken>(){}.getType()); + String[] classpaths = gson.fromJson(gson.toJson(arguments.get(1)), String[].class); String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); @@ -151,7 +150,7 @@ public static boolean exportJar(List arguments, IProgressMonitor monitor } else { File folder = new File(classpath); - recursiveFolder(folder, target, fDirectories, folder.getAbsolutePath().length() + 1); + writeFileRecursively(folder, target, fDirectories, folder.getAbsolutePath().length() + 1); } } } @@ -161,14 +160,14 @@ public static boolean exportJar(List arguments, IProgressMonitor monitor return true; } - private static void recursiveFolder(File folder, JarOutputStream fJarOutputStream, Set fDirectories, int len){ + private static void writeFileRecursively(File folder, JarOutputStream fJarOutputStream, Set fDirectories, int len){ File[] files = folder.listFiles(); for(File file : files){ if(file.isDirectory()) { - recursiveFolder(file, fJarOutputStream, fDirectories, len); + writeFileRecursively(file, fJarOutputStream, fDirectories, len); } else if(file.isFile()) { try { - write(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories); + writeFile(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories); } catch (Exception e){ // do nothing diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java index 1a0d40f9..b38b613b 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java @@ -1,3 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Microsoft Corporation - based this file on JarWriter3, JarWriter4, UnpackFatJarBuilder, JarPackagerUtil and JarBuilder + *******************************************************************************/ package org.eclipse.jdt.internal.jarpackager; import java.io.File; @@ -15,26 +29,34 @@ import java.util.zip.ZipException; import java.util.zip.ZipFile; -import org.eclipse.core.runtime.*; -import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin; -import org.eclipse.jdt.internal.ui.IJavaStatusConstants; +import com.microsoft.jdtls.ext.core.JdtlsExtActivator; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; public class JarPackageUtil { + private static final int INTERNAL_ERROR= 10001; + /** * Write the given entry describing the given content to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param entry the entry to write - * @param content the content to write - * @param fJarOutputStream the destination JarOutputStream + * @param entry the entry to write + * @param content the content to write + * @param fJarOutputStream the destination JarOutputStream * - * @throws IOException If an I/O error occurred + * @throws IOException If an I/O error occurred * - * @since 1.14 + * @since 1.14 * */ - public static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { + private static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { byte[] readBuffer= new byte[4096]; try { fJarOutputStream.putNextEntry(entry); @@ -58,22 +80,22 @@ public static void addEntry(JarEntry entry, InputStream content, JarOutputStream * Write the contents of the given zipfile to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder * - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories - * @param progressMonitor the progressMonitor + * @param progressMonitor the progressMonitor * - * @return the MultiStatus saving the warnings during the process + * @return the MultiStatus saving the warnings during the process * - * @since 1.14 + * @since 1.14 * */ public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntriesIncluded, - boolean isCompressed, JarOutputStream fJarOutputStream, - Set fDirectories, IProgressMonitor progressMonitor) { - MultiStatus fStatus = new MultiStatus(JavaManipulationPlugin.getPluginId(), IStatus.OK, ""); //$NON-NLS-1$ + boolean isCompressed, JarOutputStream fJarOutputStream, + Set fDirectories, IProgressMonitor progressMonitor) { + MultiStatus fStatus = new MultiStatus(JdtlsExtActivator.PLUGIN_ID, IStatus.OK, ""); //$NON-NLS-1$ Enumeration jarEntriesEnum= zipFile.entries(); File zipFile1 = new File(zipFile.getName()); try { @@ -107,21 +129,21 @@ public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntr * Write the entry to the destinationPath of the given JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder * - * @param destinationPath the destinationPath in the jar file - * @param jarEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream + * @param destinationPath the destinationPath in the jar file + * @param jarEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories * @param fStatus the MultiStatus saving the warnings during the process * - * @since 1.14 + * @since 1.14 * */ private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile zipFile, - boolean areDirectoryEntriesIncluded, boolean isCompressed, - JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus){ + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus){ // Handle META-INF/MANIFEST.MF if (destinationPath.equalsIgnoreCase("META-INF/MANIFEST.MF") //$NON-NLS-1$ || (destinationPath.startsWith("META-INF/") && destinationPath.endsWith(".SF"))) { //$NON-NLS-1$//$NON-NLS-2$ @@ -144,22 +166,22 @@ private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile z * Write the entry to the destinationPath of the given JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param zipEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream + * @param zipEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories * - * @throws IOException If an I/O error occurred + * @throws IOException If an I/O error occurred * - * @since 1.14 + * @since 1.14 * */ - public static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, - boolean areDirectoryEntriesIncluded, boolean isCompressed, - JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { if (areDirectoryEntriesIncluded) addDirectories(path, fJarOutputStream, fDirectories); @@ -186,15 +208,15 @@ public static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, * Creates the directory entries for the given path and writes it to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param destPath the path to add - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param destPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 */ - public static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + private static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { String path= destPath.replace(File.separatorChar, '/'); int lastSlash= path.lastIndexOf('/'); List directories= new ArrayList<>(2); @@ -222,27 +244,27 @@ public static void addDirectories(String destPath, JarOutputStream fJarOutputStr * Write the single file to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param file the file to write - * @param destinationPath the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream + * @param file the file to write + * @param destinationPath the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories * - * @throws CoreException if an error has occurred + * @throws CoreException if an error has occurred * - * @since 1.14 + * @since 1.14 * */ - public static void write(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, + public static void writeFile(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories) throws CoreException { try { addFile(file, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); } catch (IOException ex) { // Ensure full path is visible - //String message= null; + /*String message= null; //IPath path= new Path(file.getAbsolutePath()); - /*if (ex.getLocalizedMessage() != null) + if (ex.getLocalizedMessage() != null) message= Messages.format(JarPackagerMessagesCore.JarWriter_writeProblemWithMessage, new Object[] { BasicElementLabels.getPathLabel(path, false), ex.getLocalizedMessage() }); else @@ -257,16 +279,16 @@ public static void write(File file, IPath destinationPath, boolean areDirectoryE * Add the single file to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param file the file to write - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream + * @param file the file to write + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar + * @param isCompressed whether the jar is compressed or not + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 * */ private static void addFile(File file, IPath path, boolean areDirectoryEntriesIncluded, @@ -292,15 +314,15 @@ private static void addFile(File file, IPath path, boolean areDirectoryEntriesIn * Creates the directory entries for the given path and writes it to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param destinationPath the path to add - * @param fJarOutputStream the destination JarOutputStream + * @param destinationPath the path to add + * @param fJarOutputStream the destination JarOutputStream * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 */ - public static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + private static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { addDirectories(destinationPath.toString(), fJarOutputStream, fDirectories); } @@ -308,15 +330,15 @@ public static void addDirectories(IPath destinationPath, JarOutputStream fJarOut * Calculates the crc and size of the resource and updates the entry. * Extracted from org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil * - * @param entry the jar entry to update - * @param stream the input stream - * @param buffer a shared buffer to store temporary data + * @param entry the jar entry to update + * @param stream the input stream + * @param buffer a shared buffer to store temporary data * - * @throws IOException if an input/output error occurs + * @throws IOException if an input/output error occurs * - * @since 1.14 + * @since 1.14 */ - public static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { + private static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { int size= 0; final CRC32 crc= new CRC32(); int count; @@ -341,14 +363,14 @@ public static void calculateCrcAndSize(final ZipEntry entry, final InputStream s /** * add a warning message into the MultiStatus. * - * @param message the message to add - * @param error the reason of the message - * @param fStatus the MultiStatus to write + * @param message the message to add + * @param error the reason of the message + * @param fStatus the MultiStatus to write * - * @since 1.14 + * @since 1.14 */ - protected final static void addWarning(String message, Throwable error, MultiStatus fStatus) { - fStatus.add(new Status(IStatus.WARNING, JavaManipulationPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, error)); + private final static void addWarning(String message, Throwable error, MultiStatus fStatus) { + fStatus.add(new Status(IStatus.WARNING, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, message, error)); } } diff --git a/jdtls.ext/pom.xml b/jdtls.ext/pom.xml index 1c09a851..d8b15320 100644 --- a/jdtls.ext/pom.xml +++ b/jdtls.ext/pom.xml @@ -81,9 +81,9 @@ - 202009 + 202006 p2 - http://download.eclipse.org/releases/2020-09/ + http://download.eclipse.org/releases/2020-06/ oss.sonatype.org diff --git a/jdtls.ext/target.target b/jdtls.ext/target.target index 3c3a5aaa..db205200 100644 --- a/jdtls.ext/target.target +++ b/jdtls.ext/target.target @@ -1,11 +1,7 @@ - + - - - - @@ -14,14 +10,6 @@ - - - - - - - - diff --git a/package.json b/package.json index dd4feeb6..f4efb48e 100644 --- a/package.json +++ b/package.json @@ -261,22 +261,22 @@ }, { "command": "java.project.addLibraries", - "when": "view == javaProjectExplorer && viewItem =~ /java:container/referenced-libraries$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:container\/referenced-libraries$/", "group": "inline@0" }, { "command": "java.project.removeLibrary", - "when": "view == javaProjectExplorer && viewItem =~ /java:jar/referenced-libraries\\+uri$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:jar\/referenced-libraries\\+uri$/", "group": "inline" }, { "command": "java.project.refreshLibraries", - "when": "view == javaProjectExplorer && viewItem =~ /java:container/referenced-libraries$/", + "when": "view == javaProjectExplorer && viewItem =~ /java:container\/referenced-libraries$/", "group": "inline@1" }, { "command": "java.project.maven.addDependency", - "when": "view == javaProjectExplorer && mavenEnabled && viewItem =~ /container/maven-dependencies/", + "when": "view == javaProjectExplorer && mavenEnabled && viewItem =~ /container\/maven-dependencies/", "group": "inline@0" }, { diff --git a/package.nls.zh.json b/package.nls.zh.json index bb5a7693..ed5f35b7 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -11,7 +11,7 @@ "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.exportJar": "导出到Jar文件", + "contributes.commands.java.view.package.exportJar": "导出到 Jar 文件", "contributes.commands.java.view.package.copyFilePath": "复制路径", "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", diff --git a/src/anchor.ts b/src/anchor.ts deleted file mode 100644 index 145c3d5d..00000000 --- a/src/anchor.ts +++ /dev/null @@ -1,10 +0,0 @@ - -export const LS_FAILS_TO_START = "java-language-support-extension-fails-to-start"; -export const FAILED_TO_RESOLVE_CLASSPATH = "failed-to-resolve-classpath"; -export const REQUEST_TYPE_NOT_SUPPORTED = "request-type-xyz-is-not-supported-only-launch-and-attach-are-supported"; -export const FAILED_TO_COMPLETE_HCR = "failed-to-complete-hot-code-replace"; -export const ATTACH_CONFIG_ERROR = "please-specify-the-host-name-and-the-port-of-the-remote-debuggee-in-the-launchjson"; -export const EVALUATE_ON_RUNNING_THREAD = "failed-to-evaluate-reason-cannot-evaluate-because-the-thread-is-resumed"; -export const CANNOT_FIND_MAIN_CLASS = "cannot-find-a-class-with-the-main-method"; -export const BUILD_FAILED = "build-failed-do-you-want-to-continue"; -export const UNSUPPORTED_CLASS_VERSION_ERROR = "program-throws-unsupportedclassversionerror"; diff --git a/src/commands.ts b/src/commands.ts index 645253fa..cf71d73a 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,11 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - +import { commands } from "vscode"; +import { getJavaExtension, JavaExtensionNotEnabledError } from "./utility"; /** * Commonly used commands */ -import { commands } from "vscode"; -import { getJavaExtension, JavaExtensionNotEnabledError } from "./utility"; export namespace Commands { /** * Execute Workspace Command @@ -33,6 +32,7 @@ export namespace Commands { export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar"; + export const JAVA_PROJECT_CREATE = "java.project.create"; export const JAVA_PROJECT_ADD_LIBRARIES = "java.project.addLibraries"; @@ -63,6 +63,7 @@ export namespace Commands { export const JAVA_PROJECT_GETMAINMETHOD = "java.project.getMainMethod"; export const JAVA_PROJECT_EXPORTJAR = "java.project.exportJar"; + export const VSCODE_OPEN_FOLDER = "vscode.openFolder"; } @@ -70,30 +71,8 @@ export const JAVA_BUILD_WORKSPACE = "java.workspace.compile"; export const JAVA_EXECUTE_WORKSPACE_COMMAND = "java.execute.workspaceCommand"; -export const JAVA_RESOLVE_MAINMETHOD = "vscode.java.resolveMainMethod"; - -export const JAVA_START_DEBUGSESSION = "vscode.java.startDebugSession"; - -export const JAVA_RESOLVE_CLASSPATH = "vscode.java.resolveClasspath"; - -export const JAVA_RESOLVE_MAINCLASS = "vscode.java.resolveMainClass"; - -export const JAVA_VALIDATE_LAUNCHCONFIG = "vscode.java.validateLaunchConfig"; - -export const JAVA_INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandLength"; - -export const JAVA_CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings"; - -export const JAVA_RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection"; - export const JAVA_RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; -export const JAVA_IS_ON_CLASSPATH = "vscode.java.isOnClasspath"; - -export const JAVA_RESOLVE_JAVAEXECUTABLE = "vscode.java.resolveJavaExecutable"; - -export const JAVA_FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings"; - export function executeJavaLanguageServerCommand(...rest) { return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest); } @@ -108,4 +87,4 @@ export async function executeJavaExtensionCommand(commandName: string, ...rest) await javaExtension.activate(); } return commands.executeCommand(commandName, ...rest); -} +} \ No newline at end of file diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts index 1ba21f14..51f1dd20 100644 --- a/src/languageServerPlugin.ts +++ b/src/languageServerPlugin.ts @@ -1,8 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. - -import * as vscode from "vscode"; - import * as commands from "./commands"; export enum CompileWorkspaceStatus { @@ -12,95 +9,6 @@ export enum CompileWorkspaceStatus { CANCELLED = 3, } -export interface IMainClassOption { - readonly mainClass: string; - readonly projectName?: string; - readonly filePath?: string; -} - -export interface IMainMethod extends IMainClassOption { - range: vscode.Range; -} - -export interface IValidationResult { - readonly isValid: boolean; - readonly message?: string; -} - -export interface ILaunchValidationResponse { - readonly mainClass: IValidationResult; - readonly projectName: IValidationResult; - readonly proposals?: IMainClassOption[]; -} - -export async function resolveMainMethod(uri: vscode.Uri): Promise { - return await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINMETHOD, uri.toString()); -} - -export function startDebugSession() { - return commands.executeJavaLanguageServerCommand(commands.JAVA_START_DEBUGSESSION); -} - -export function resolveClasspath(mainClass, projectName) { - return commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_CLASSPATH, mainClass, projectName); -} - -export function resolveMainClass(workspaceUri: vscode.Uri): Promise { - if (workspaceUri) { - return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINCLASS, workspaceUri.toString()); - } - return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_MAINCLASS); -} - -export function validateLaunchConfig(workspaceUri: vscode.Uri, mainClass: string, projectName: string, containsExternalClasspaths: boolean): - Promise { - return >commands.executeJavaLanguageServerCommand(commands.JAVA_VALIDATE_LAUNCHCONFIG, - workspaceUri ? workspaceUri.toString() : undefined, mainClass, projectName, containsExternalClasspaths); -} - -export function inferLaunchCommandLength(config: vscode.DebugConfiguration): Promise { - return >commands.executeJavaLanguageServerCommand(commands.JAVA_INFER_LAUNCH_COMMAND_LENGTH, JSON.stringify(config)); -} - -export function checkProjectSettings(className: string, projectName: string, inheritedOptions: boolean, expectedOptions: {[key: string]: string}): -Promise { - return >commands.executeJavaLanguageServerCommand( - commands.JAVA_CHECK_PROJECT_SETTINGS, JSON.stringify({ - className, - projectName, - inheritedOptions, - expectedOptions, - })); -} - -const COMPILER_PB_ENABLE_PREVIEW_FEATURES: string = "org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures"; -export async function detectPreviewFlag(className: string, projectName: string): Promise { - const expectedOptions = { - [COMPILER_PB_ENABLE_PREVIEW_FEATURES]: "enabled", - }; - return checkProjectSettings(className, projectName, true, expectedOptions); -} - -export function resolveElementAtSelection(uri: string, line: number, character: number): Promise { - return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_ELEMENT_AT_SELECTION, uri, line, character); -} - export function resolveBuildFiles(): Promise { return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_BUILD_FILES); -} - -export async function isOnClasspath(uri: string): Promise { - try { - return await commands.executeJavaExtensionCommand(commands.JAVA_IS_ON_CLASSPATH, uri); - } catch (error) { - return true; - } -} - -export function resolveJavaExecutable(mainClass, projectName) { - return commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_JAVAEXECUTABLE, mainClass, projectName); -} - -export function fetchPlatformSettings(): any { - return commands.executeJavaLanguageServerCommand(commands.JAVA_FETCH_PLATFORM_SETTINGS); -} +} \ No newline at end of file diff --git a/src/logger.ts b/src/logger.ts index f765b4b4..ab645361 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -58,4 +58,4 @@ class Logger implements vscode.Disposable { } } -export const logger = new Logger(); +export const logger = new Logger(); \ No newline at end of file diff --git a/src/utility.ts b/src/utility.ts index 50787405..9ce85d66 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import * as vscode from "vscode"; import { commands, Extension, extensions, ProgressLocation, Uri, window, workspace, WorkspaceFolder } from "vscode"; import { setUserError } from "vscode-extension-telemetry-wrapper"; import { logger, Type } from "./logger"; @@ -26,18 +27,15 @@ export class Utility { } -interface IProperties { - [key: string]: string; -} - -interface ILoggingMessage { - message: string; - type?: Type; - details?: IProperties; +export function getJavaExtension(): Extension { + return extensions.getExtension(JAVA_EXTENSION_ID); } -interface ITroubleshootingMessage extends ILoggingMessage { - anchor?: string; +export class JavaExtensionNotEnabledError extends Error { + constructor(message) { + super(message); + setUserError(this); + } } export class UserError extends Error { @@ -50,42 +48,24 @@ export class UserError extends Error { } } -export function getJavaExtension(): Extension { - return extensions.getExtension(JAVA_EXTENSION_ID); -} - -export class JavaExtensionNotEnabledError extends Error { - constructor(message) { - super(message); - setUserError(this); - } +interface IProperties { + [key: string]: string; } -export async function guideToInstallJavaExtension() { - const MESSAGE = "Language Support for Java is required. Please install and enable it."; - const INSTALL = "Install"; - const choice = await window.showWarningMessage(MESSAGE, INSTALL); - if (choice === INSTALL) { - await installJavaExtension(); - } +interface ILoggingMessage { + message: string; + type?: Type; + details?: IProperties; } -async function installJavaExtension() { - await window.withProgress({ location: ProgressLocation.Notification }, async (p) => { - p.report({ message: "Installing Language Support for Java ..." }); - await commands.executeCommand("workbench.extensions.installExtension", JAVA_EXTENSION_ID); - }); - const RELOAD = "Reload Window"; - const choice = await window.showInformationMessage("Please reload window to activate Language Support for Java.", RELOAD); - if (choice === RELOAD) { - await commands.executeCommand("workbench.action.reloadWindow"); - } +interface ITroubleshootingMessage extends ILoggingMessage { + anchor?: string; } export function openTroubleshootingPage(message: string, anchor: string) { - commands.executeCommand("vscode.open", Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); + vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); logger.log(Type.USAGEDATA, { troubleshooting: "yes", troubleshootingMessage: message, }); -} +} \ No newline at end of file diff --git a/src/views/build.ts b/src/views/build.ts index 51eaf9f9..f5b794e1 100644 --- a/src/views/build.ts +++ b/src/views/build.ts @@ -1,8 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + import * as path from "path"; import * as vscode from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; -import * as anchor from "../anchor"; import * as commands from "../commands"; import * as lsPlugin from "../languageServerPlugin"; import * as utility from "../utility"; @@ -29,10 +31,6 @@ export async function buildWorkspace(): Promise { } async function handleBuildFailure(operationId: string, err: any): Promise { - if (err instanceof utility.JavaExtensionNotEnabledError) { - utility.guideToInstallJavaExtension(); - return false; - } const error: Error = new utility.UserError({ message: "Build failed", @@ -55,10 +53,8 @@ async function handleBuildFailure(operationId: string, err: any): Promise { this.successMessage(message); }, () => {}); } - private static solveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { + private static resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { return new Promise((resolve, reject) => { if (token.isCancellationRequested) { return reject(); @@ -99,12 +100,12 @@ export class ExportJarFile { if (folders.length === 1) { return resolve(folders[0]); } - progress.report({ increment: 10, message: "Determining project..." }); - const pickNodes: ProjectPickNode[] = []; + progress.report({ increment: 10, message: "Selecting project..." }); + const pickNodes: QuickPickNode[] = []; for (const folder of folders) { - pickNodes.push(new ProjectPickNode(folder.name, folder.uri.fsPath, folder.uri.fsPath)); + pickNodes.push(new QuickPickNode(folder.name, folder.uri.fsPath, folder.uri.fsPath)); } - const pickBox = window.createQuickPick(); + const pickBox = window.createQuickPick(); pickBox.items = pickNodes; pickBox.title = "Export Jar - Determine project"; pickBox.placeholder = "Select the project..."; @@ -131,8 +132,8 @@ export class ExportJarFile { }); } - private static writingJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], - description: string, outputPath: string): Promise { + private static generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], + description: string, outputPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { return reject(); @@ -140,7 +141,7 @@ export class ExportJarFile { this.failMessage("No module found in this project"); return reject(); } - progress.report({ increment: 10, message: "Solving classpaths..." }); + progress.report({ increment: 10, message: "Resolving classpaths..." }); let outClassPaths: string[]; try { outClassPaths = await this.generateOutClassPath(pickSteps, rootNodes, outputPath); @@ -158,12 +159,12 @@ export class ExportJarFile { }); } - private static solveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { + private static resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { return reject(); } - progress.report({ increment: 10, message: "Getting main classes..." }); + progress.report({ increment: 10, message: "Resolving main classes..." }); if (this.mainMethods === undefined || this.mainMethods.length === 0) { return resolve(""); } @@ -209,7 +210,14 @@ export class ExportJarFile { } private static successMessage(outputFileName: string) { - const openInExplorer: Message = new Message("Reveal in File Explorer"); + let openInExplorer: Message; + if(platform() === "win32") { + openInExplorer = new Message("Reveal in File Explorer"); + } else if(platform() === "darwin") { + openInExplorer = new Message("Reveal in Finder"); + } else { + openInExplorer = new Message("Open Containing Folder"); + } window.showInformationMessage("Successfully exported jar to" + EOL + outputFileName, new Option(false), openInExplorer, new Message("Done", true)).then((messageResult) => { if (messageResult === openInExplorer) { @@ -224,17 +232,17 @@ export class ExportJarFile { const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - const pickDependencies: ElementPickNode[] = []; - const pickedDependencies: ElementPickNode[] = []; + const pickDependencies: QuickPickNode[] = []; + const pickedDependencies: QuickPickNode[] = []; for (const rootNode of rootNodes) { const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - this.generateDependList(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); - this.generateDependList(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); + this.generateDependencies(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); + this.generateDependencies(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); const modulePathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - this.generateDependList(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); - this.generateDependList(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); + this.generateDependencies(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); + this.generateDependencies(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); } - const pickBox = window.createQuickPick(); + const pickBox = window.createQuickPick(); pickDependencies.sort((node1, node2) => { if (node1.description !== node2.description) { return node1.description.localeCompare(node2.description); @@ -277,8 +285,8 @@ export class ExportJarFile { }); } - private static generateDependList(paths: string[], setUris: Set, pickDependencies: ElementPickNode[], - projectPath: string, isRuntime: boolean) { + private static generateDependencies(paths: string[], setUris: Set, pickDependencies: QuickPickNode[], + projectPath: string, isRuntime: boolean) { paths.forEach((classpath: string) => { const extName = extname(classpath); const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); @@ -286,7 +294,7 @@ export class ExportJarFile { const type = (extName === ".jar") ? "external" : "internal"; if (!setUris.has(classpath)) { setUris.add(classpath); - pickDependencies.push(new ElementPickNode(baseName, description, classpath, type, isRuntime)); + pickDependencies.push(new QuickPickNode(baseName, description, classpath, type, isRuntime)); } }); } diff --git a/src/views/projectPickNode.ts b/src/views/projectPickNode.ts deleted file mode 100644 index c2df6324..00000000 --- a/src/views/projectPickNode.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { QuickPickNode } from "./quickPickNode"; - -export class ProjectPickNode extends QuickPickNode { - - public uri: string; - - constructor(_label: string, _description: string, _uri: string) { - super(_label, _description); - this.uri = _uri; - } -} diff --git a/src/views/quickPickNode.ts b/src/views/quickPickNode.ts index 02b964e8..c18dae43 100644 --- a/src/views/quickPickNode.ts +++ b/src/views/quickPickNode.ts @@ -1,13 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + import { QuickPickItem } from "vscode"; export class QuickPickNode implements QuickPickItem { public label: string; public description: string; + public uri?: string; + public type?: string; + public picked?: boolean; - constructor(_label: string, _description: string) { + constructor(_label: string, _description: string, _uri?: string, _type?: string, _picked?: boolean) { this.label = _label; this.description = _description; + this.uri = _uri; + this.type = _type; + this.picked = _picked; } } From c9bdc4f39951ccc2f6a57bf9e3c6fa1d5f570cd6 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 20 Jul 2020 10:30:41 +0800 Subject: [PATCH 12/44] Fix tslint error --- src/commands.ts | 4 ++-- src/languageServerPlugin.ts | 2 +- src/logger.ts | 2 +- src/utility.ts | 7 +++---- src/views/exportJarFile.ts | 8 ++++---- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index cf71d73a..61eb519d 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -32,7 +32,7 @@ export namespace Commands { export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar"; - + export const JAVA_PROJECT_CREATE = "java.project.create"; export const JAVA_PROJECT_ADD_LIBRARIES = "java.project.addLibraries"; @@ -87,4 +87,4 @@ export async function executeJavaExtensionCommand(commandName: string, ...rest) await javaExtension.activate(); } return commands.executeCommand(commandName, ...rest); -} \ No newline at end of file +} diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts index 51f1dd20..921b9a09 100644 --- a/src/languageServerPlugin.ts +++ b/src/languageServerPlugin.ts @@ -11,4 +11,4 @@ export enum CompileWorkspaceStatus { export function resolveBuildFiles(): Promise { return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_BUILD_FILES); -} \ No newline at end of file +} diff --git a/src/logger.ts b/src/logger.ts index ab645361..f765b4b4 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -58,4 +58,4 @@ class Logger implements vscode.Disposable { } } -export const logger = new Logger(); \ No newline at end of file +export const logger = new Logger(); diff --git a/src/utility.ts b/src/utility.ts index 9ce85d66..39df35aa 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import * as vscode from "vscode"; -import { commands, Extension, extensions, ProgressLocation, Uri, window, workspace, WorkspaceFolder } from "vscode"; +import { commands, Extension, extensions, Uri, window, workspace, WorkspaceFolder } from "vscode"; import { setUserError } from "vscode-extension-telemetry-wrapper"; import { logger, Type } from "./logger"; const JAVA_EXTENSION_ID = "redhat.java"; @@ -63,9 +62,9 @@ interface ITroubleshootingMessage extends ILoggingMessage { } export function openTroubleshootingPage(message: string, anchor: string) { - vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); + commands.executeCommand("vscode.open", Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); logger.log(Type.USAGEDATA, { troubleshooting: "yes", troubleshootingMessage: message, }); -} \ No newline at end of file +} diff --git a/src/views/exportJarFile.ts b/src/views/exportJarFile.ts index 66648e72..62bd7847 100644 --- a/src/views/exportJarFile.ts +++ b/src/views/exportJarFile.ts @@ -133,7 +133,7 @@ export class ExportJarFile { } private static generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], - description: string, outputPath: string): Promise { + description: string, outputPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { return reject(); @@ -211,9 +211,9 @@ export class ExportJarFile { private static successMessage(outputFileName: string) { let openInExplorer: Message; - if(platform() === "win32") { + if (platform() === "win32") { openInExplorer = new Message("Reveal in File Explorer"); - } else if(platform() === "darwin") { + } else if (platform() === "darwin") { openInExplorer = new Message("Reveal in Finder"); } else { openInExplorer = new Message("Open Containing Folder"); @@ -286,7 +286,7 @@ export class ExportJarFile { } private static generateDependencies(paths: string[], setUris: Set, pickDependencies: QuickPickNode[], - projectPath: string, isRuntime: boolean) { + projectPath: string, isRuntime: boolean) { paths.forEach((classpath: string) => { const extName = extname(classpath); const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); From c4e2ee2afaf76efe45895ef1730b2e813406ef82 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 20 Jul 2020 10:32:25 +0800 Subject: [PATCH 13/44] Merge branch 'master' of https://github.com/CsCherrYY/vscode-java-dependency --- package-lock.json | 33 ++++++++++++++++++--------------- package.json | 2 +- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index e69487e8..98af8c03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -320,7 +320,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -860,7 +860,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1053,7 +1053,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -1709,7 +1710,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -2685,7 +2686,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -4109,7 +4110,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, "liftoff": { @@ -4175,7 +4177,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -5086,9 +5088,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, "lodash.debounce": { "version": "4.0.8", @@ -6529,7 +6531,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -6972,7 +6974,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -7319,7 +7321,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -7787,7 +7789,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -9327,7 +9330,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/package.json b/package.json index f4efb48e..035ff01a 100644 --- a/package.json +++ b/package.json @@ -328,7 +328,7 @@ }, "dependencies": { "fs-extra": "^7.0.1", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "minimatch": "^3.0.4", "vscode-extension-telemetry-wrapper": "0.8.0", "vscode-tas-client": "0.0.659" From f3537966d6673e54e6c9a84e63b70c03b05a33b6 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 20 Jul 2020 15:03:09 +0800 Subject: [PATCH 14/44] Apply changes --- .../jdtls/ext/core/ProjectCommand.java | 28 +- .../internal/jarpackager/JarPackageUtil.java | 185 +++++----- src/languageServerPlugin.ts | 8 +- src/views/build.ts | 3 +- src/views/dependencyDataProvider.ts | 5 +- src/views/exportJarFile.ts | 335 ------------------ src/views/exportJarFileUtil.ts | 322 +++++++++++++++++ .../{quickPickNode.ts => jarQuickPickItem.ts} | 2 +- 8 files changed, 431 insertions(+), 457 deletions(-) delete mode 100644 src/views/exportJarFile.ts create mode 100644 src/views/exportJarFileUtil.ts rename src/views/{quickPickNode.ts => jarQuickPickItem.ts} (90%) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index bb4c5eae..e3b5ebaf 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -129,7 +129,7 @@ private static String getWorkspaceInvisibleProjectName(IPath workspacePath) { } public static boolean exportJar(List arguments, IProgressMonitor monitor) { - if(arguments.size() < 3) { + if (arguments.size() < 3) { return false; } String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class); @@ -137,16 +137,16 @@ public static boolean exportJar(List arguments, IProgressMonitor monitor String destination = gson.fromJson(gson.toJson(arguments.get(2)), String.class); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); - if(mainMethod.length() > 0) { + if (mainMethod.length() > 0) { manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS,mainMethod); } - try (JarOutputStream target = new JarOutputStream(new FileOutputStream(destination), manifest)){ + try (JarOutputStream target = new JarOutputStream(new FileOutputStream(destination), manifest)) { Set fDirectories = new HashSet<>(); - for(String classpath : classpaths){ - if (classpath != null){ - if(classpath.endsWith(".jar")){ + for (String classpath : classpaths) { + if (classpath != null) { + if(classpath.endsWith(".jar")) { ZipFile zip = new ZipFile(classpath); - writeArchive(zip, true, true, target, fDirectories,monitor); + writeArchive(zip, true, true, target, fDirectories, monitor); } else { File folder = new File(classpath); @@ -154,29 +154,29 @@ public static boolean exportJar(List arguments, IProgressMonitor monitor } } } - } catch (Exception e){ + } catch (Exception e) { return false; } return true; } - private static void writeFileRecursively(File folder, JarOutputStream fJarOutputStream, Set fDirectories, int len){ + private static void writeFileRecursively(File folder, JarOutputStream fJarOutputStream, Set fDirectories, int len) { File[] files = folder.listFiles(); - for(File file : files){ - if(file.isDirectory()) { + for (File file : files) { + if (file.isDirectory()) { writeFileRecursively(file, fJarOutputStream, fDirectories, len); - } else if(file.isFile()) { + } else if (file.isFile()) { try { writeFile(file, new Path(file.getAbsolutePath().substring(len)), true, true, fJarOutputStream, fDirectories); } - catch (Exception e){ + catch (Exception e) { // do nothing } } } } - public static List getMainMethod(IProgressMonitor monitor) throws Exception{ + public static List getMainMethod(IProgressMonitor monitor) throws Exception { final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java index b38b613b..e34c4be2 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java @@ -47,21 +47,21 @@ public class JarPackageUtil { * Write the given entry describing the given content to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param entry the entry to write - * @param content the content to write - * @param fJarOutputStream the destination JarOutputStream + * @param entry the entry to write + * @param content the content to write + * @param fJarOutputStream the destination JarOutputStream * - * @throws IOException If an I/O error occurred + * @throws IOException If an I/O error occurred * - * @since 1.14 + * @since 1.14 * */ private static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { - byte[] readBuffer= new byte[4096]; + byte[] readBuffer = new byte[4096]; try { fJarOutputStream.putNextEntry(entry); int count; - while ((count= content.read(readBuffer, 0, readBuffer.length)) != -1) + while ((count = content.read(readBuffer, 0, readBuffer.length)) != -1) fJarOutputStream.write(readBuffer, 0, count); } finally { if (content != null) @@ -80,31 +80,31 @@ private static void addEntry(JarEntry entry, InputStream content, JarOutputStrea * Write the contents of the given zipfile to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder * - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * @param progressMonitor the progressMonitor + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param progressMonitor the progressMonitor * - * @return the MultiStatus saving the warnings during the process + * @return the MultiStatus saving the warnings during the process * - * @since 1.14 + * @since 1.14 * */ public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntriesIncluded, boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories, IProgressMonitor progressMonitor) { - MultiStatus fStatus = new MultiStatus(JdtlsExtActivator.PLUGIN_ID, IStatus.OK, ""); //$NON-NLS-1$ - Enumeration jarEntriesEnum= zipFile.entries(); + MultiStatus fStatus = new MultiStatus(JdtlsExtActivator.PLUGIN_ID, IStatus.OK, ""); //$NON-NLS-1$ + Enumeration jarEntriesEnum = zipFile.entries(); File zipFile1 = new File(zipFile.getName()); try { String zipFileCanonical = zipFile1.getCanonicalPath(); while (jarEntriesEnum.hasMoreElements()) { - ZipEntry zipEntry= jarEntriesEnum.nextElement(); + ZipEntry zipEntry = jarEntriesEnum.nextElement(); if (!zipEntry.isDirectory()) { - String entryName= zipEntry.getName(); + String entryName = zipEntry.getName(); File zipEntryFile = new File(zipFile1, entryName); String zipEntryCanonical = zipEntryFile.getCanonicalPath(); if (zipEntryCanonical.startsWith(zipFileCanonical + File.separator)) { @@ -129,21 +129,21 @@ public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntr * Write the entry to the destinationPath of the given JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder * - * @param destinationPath the destinationPath in the jar file - * @param jarEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * @param fStatus the MultiStatus saving the warnings during the process + * @param destinationPath the destinationPath in the jar file + * @param jarEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param fStatus the MultiStatus saving the warnings during the process * - * @since 1.14 + * @since 1.14 * */ private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile zipFile, boolean areDirectoryEntriesIncluded, boolean isCompressed, - JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus){ + JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus) { // Handle META-INF/MANIFEST.MF if (destinationPath.equalsIgnoreCase("META-INF/MANIFEST.MF") //$NON-NLS-1$ || (destinationPath.startsWith("META-INF/") && destinationPath.endsWith(".SF"))) { //$NON-NLS-1$//$NON-NLS-2$ @@ -157,8 +157,7 @@ private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile z if (!destinationPath.startsWith("META-INF/")) { //$NON-NLS-1$ addWarning(ex.getMessage(), ex, fStatus); } - } //else - //addWarning(Messages.format(JarPackagerMessagesCore.FatJarBuilder_error_readingArchiveFile, new Object[] { BasicElementLabels.getResourceName(zipFile.getName()), ex.getLocalizedMessage() }), ex, fStatus); + } } } @@ -166,17 +165,17 @@ private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile z * Write the entry to the destinationPath of the given JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param zipEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param zipEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws IOException If an I/O error occurred + * @throws IOException If an I/O error occurred * - * @since 1.14 + * @since 1.14 * */ private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, @@ -185,7 +184,7 @@ private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, if (areDirectoryEntriesIncluded) addDirectories(path, fJarOutputStream, fDirectories); - JarEntry newEntry= new JarEntry(path.replace(File.separatorChar, '/')); + JarEntry newEntry = new JarEntry(path.replace(File.separatorChar, '/')); if (isCompressed) newEntry.setMethod(ZipEntry.DEFLATED); @@ -196,7 +195,7 @@ private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, newEntry.setCrc(zipEntry.getCrc()); } - long lastModified= System.currentTimeMillis(); + long lastModified = System.currentTimeMillis(); // Set modification time newEntry.setTime(lastModified); @@ -208,24 +207,24 @@ private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, * Creates the directory entries for the given path and writes it to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param destPath the path to add - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param destPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 */ private static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { - String path= destPath.replace(File.separatorChar, '/'); - int lastSlash= path.lastIndexOf('/'); - List directories= new ArrayList<>(2); + String path = destPath.replace(File.separatorChar, '/'); + int lastSlash = path.lastIndexOf('/'); + List directories = new ArrayList<>(2); while (lastSlash != -1) { path= path.substring(0, lastSlash + 1); if (!fDirectories.add(path)) break; - JarEntry newEntry= new JarEntry(path); + JarEntry newEntry = new JarEntry(path); newEntry.setMethod(ZipEntry.STORED); newEntry.setSize(0); newEntry.setCrc(0); @@ -235,7 +234,7 @@ private static void addDirectories(String destPath, JarOutputStream fJarOutputSt lastSlash= path.lastIndexOf('/', lastSlash - 1); } - for (int i= directories.size() - 1; i >= 0; --i) { + for (int i = directories.size() - 1; i >= 0; --i) { fJarOutputStream.putNextEntry(directories.get(i)); } } @@ -244,16 +243,16 @@ private static void addDirectories(String destPath, JarOutputStream fJarOutputSt * Write the single file to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param file the file to write - * @param destinationPath the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param file the file to write + * @param destinationPath the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws CoreException if an error has occurred + * @throws CoreException if an error has occurred * - * @since 1.14 + * @since 1.14 * */ public static void writeFile(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, @@ -261,17 +260,7 @@ public static void writeFile(File file, IPath destinationPath, boolean areDirect try { addFile(file, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); } catch (IOException ex) { - // Ensure full path is visible - /*String message= null; - //IPath path= new Path(file.getAbsolutePath()); - if (ex.getLocalizedMessage() != null) - message= Messages.format(JarPackagerMessagesCore.JarWriter_writeProblemWithMessage, - new Object[] { BasicElementLabels.getPathLabel(path, false), ex.getLocalizedMessage() }); - else - message= Messages.format(JarPackagerMessagesCore.JarWriter_writeProblem, BasicElementLabels.getPathLabel(path, false)); - if (message == null) - message= ""; //$NON-NLS-1$ - throw new CoreException(new Status(IStatus.ERROR, JavaManipulationPlugin.getPluginId(), IJavaStatusConstants.INTERNAL_ERROR, message, ex));*/ + throw new CoreException(new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, ex.getLocalizedMessage(), ex)); } } @@ -279,16 +268,16 @@ public static void writeFile(File file, IPath destinationPath, boolean areDirect * Add the single file to the JarOutputStream. * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 * - * @param file the file to write - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded Tells whether directory entries are added to the jar - * @param isCompressed whether the jar is compressed or not - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param file the file to write + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 * */ private static void addFile(File file, IPath path, boolean areDirectoryEntriesIncluded, @@ -296,7 +285,7 @@ private static void addFile(File file, IPath path, boolean areDirectoryEntriesIn if (areDirectoryEntriesIncluded) addDirectories(path, fJarOutputStream, fDirectories); - JarEntry newEntry= new JarEntry(path.toString().replace(File.separatorChar, '/')); + JarEntry newEntry = new JarEntry(path.toString().replace(File.separatorChar, '/')); if (isCompressed) newEntry.setMethod(ZipEntry.DEFLATED); @@ -314,13 +303,13 @@ private static void addFile(File file, IPath path, boolean areDirectoryEntriesIn * Creates the directory entries for the given path and writes it to the current archive. * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 * - * @param destinationPath the path to add - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories + * @param destinationPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories * - * @throws IOException if an I/O error has occurred + * @throws IOException if an I/O error has occurred * - * @since 1.14 + * @since 1.14 */ private static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { addDirectories(destinationPath.toString(), fJarOutputStream, fDirectories); @@ -330,22 +319,22 @@ private static void addDirectories(IPath destinationPath, JarOutputStream fJarOu * Calculates the crc and size of the resource and updates the entry. * Extracted from org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil * - * @param entry the jar entry to update - * @param stream the input stream - * @param buffer a shared buffer to store temporary data + * @param entry the jar entry to update + * @param stream the input stream + * @param buffer a shared buffer to store temporary data * - * @throws IOException if an input/output error occurs + * @throws IOException if an input/output error occurs * - * @since 1.14 + * @since 1.14 */ private static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { - int size= 0; - final CRC32 crc= new CRC32(); + int size = 0; + final CRC32 crc = new CRC32(); int count; try { - while ((count= stream.read(buffer, 0, buffer.length)) != -1) { + while ((count = stream.read(buffer, 0, buffer.length)) != -1) { crc.update(buffer, 0, count); - size+= count; + size += count; } } finally { if (stream != null) { @@ -363,11 +352,11 @@ private static void calculateCrcAndSize(final ZipEntry entry, final InputStream /** * add a warning message into the MultiStatus. * - * @param message the message to add - * @param error the reason of the message - * @param fStatus the MultiStatus to write + * @param message the message to add + * @param error the reason of the message + * @param fStatus the MultiStatus to write * - * @since 1.14 + * @since 1.14 */ private final static void addWarning(String message, Throwable error, MultiStatus fStatus) { fStatus.add(new Status(IStatus.WARNING, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, message, error)); diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts index 921b9a09..e8025e60 100644 --- a/src/languageServerPlugin.ts +++ b/src/languageServerPlugin.ts @@ -3,10 +3,10 @@ import * as commands from "./commands"; export enum CompileWorkspaceStatus { - FAILED = 0, - SUCCEED = 1, - WITHERROR = 2, - CANCELLED = 3, + Failed = 0, + Succeed = 1, + Witherror = 2, + Cancelled = 3, } export function resolveBuildFiles(): Promise { diff --git a/src/views/build.ts b/src/views/build.ts index f5b794e1..86c020a7 100644 --- a/src/views/build.ts +++ b/src/views/build.ts @@ -4,7 +4,6 @@ import * as path from "path"; import * as vscode from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; - import * as commands from "../commands"; import * as lsPlugin from "../languageServerPlugin"; import * as utility from "../utility"; @@ -37,7 +36,7 @@ async function handleBuildFailure(operationId: string, err: any): Promise { 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_EXPORT_JAR, (node: INodeData) => - ExportJarFile.createJarFile(node))); + context.subscriptions.push(commands.registerCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, (node: INodeData) => createJarFile(node))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => diff --git a/src/views/exportJarFile.ts b/src/views/exportJarFile.ts deleted file mode 100644 index 62bd7847..00000000 --- a/src/views/exportJarFile.ts +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { EOL, platform } from "os"; -import { basename, extname, join } from "path"; -import { CancellationToken, commands, Extension, extensions, MessageItem, MessageOptions, - ProgressLocation, QuickInputButtons, Uri, window, workspace, WorkspaceFolder } from "vscode"; - -import { isStandardServerReady } from "../extension"; -import { Jdtls } from "../java/jdtls"; -import { INodeData } from "../java/nodeData"; - -import { buildWorkspace } from "./build"; -import { QuickPickNode } from "./quickPickNode"; -import { WorkspaceNode } from "./workspaceNode"; - -const SOLVE_PROJECT = "solve project"; -const SOLVE_MAINMETHOD = "solve mainmethod"; -const GENERATE_JAR = "generate jar"; -const FINISH = "finish"; - -export class ExportJarFile { - - public static mainMethods: MainMethodInfo[]; - - public static async createJarFile(node?: INodeData) { - if (!isStandardServerReady()) { - return; - } - window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar", - cancellable: true, - }, (progress, token): Promise => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - reject(); - }); - progress.report({ increment: 10, message: "Building workspace..." }); - if (await buildWorkspace() === false) { - return reject(); - } - this.mainMethods = await Jdtls.getMainMethod(); - const pickSteps: string[] = []; - let step: string = SOLVE_PROJECT; - let rootNodes: INodeData[] = []; - let projectFolder: WorkspaceFolder; - let pickResult: string; - let outputFileName: string; - while (step !== FINISH) { - try { - switch (step) { - case SOLVE_PROJECT: { - projectFolder = await this.resolveProject(progress, token, pickSteps, node); - rootNodes = await Jdtls.getProjects(projectFolder.uri.toString()); - step = SOLVE_MAINMETHOD; - break; - } - case SOLVE_MAINMETHOD: { - pickResult = await this.resolveMainMethod(progress, token, pickSteps, projectFolder.uri.fsPath); - step = GENERATE_JAR; - break; - } - case GENERATE_JAR: { - outputFileName = await this.generateJar(progress, token, pickSteps, rootNodes, pickResult, projectFolder.uri.fsPath); - resolve(outputFileName); - step = FINISH; - break; - } - } - } catch (err) { - if (err === InputFlowAction.back) { - step = pickSteps.pop(); - continue; - } else { - return reject(); - } - } - } - }); - }).then((message) => { this.successMessage(message); }, () => {}); - } - - private static resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { - return new Promise((resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } - const folders = workspace.workspaceFolders; - let projectFolder: WorkspaceFolder; - if (node instanceof WorkspaceNode) { - folders.forEach((folder) => { - if (folder.uri.toString() === node.uri) { - return resolve(folder); - } - }); - return reject(); - } - if (folders && folders.length) { - if (folders.length === 1) { - return resolve(folders[0]); - } - progress.report({ increment: 10, message: "Selecting project..." }); - const pickNodes: QuickPickNode[] = []; - for (const folder of folders) { - pickNodes.push(new QuickPickNode(folder.name, folder.uri.fsPath, folder.uri.fsPath)); - } - const pickBox = window.createQuickPick(); - pickBox.items = pickNodes; - pickBox.title = "Export Jar - Determine project"; - pickBox.placeholder = "Select the project..."; - pickBox.ignoreFocusOut = true; - pickBox.onDidAccept(() => { - pickSteps.push(SOLVE_PROJECT); - folders.forEach((folder) => { - if (folder.uri.fsPath === pickBox.selectedItems[0].uri) { - projectFolder = folder; - } - }); - resolve(projectFolder); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - } else { - this.failMessage("No project found"); - return reject(); - } - }); - } - - private static generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], - description: string, outputPath: string): Promise { - return new Promise(async (resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } else if (rootNodes === undefined) { - this.failMessage("No module found in this project"); - return reject(); - } - progress.report({ increment: 10, message: "Resolving classpaths..." }); - let outClassPaths: string[]; - try { - outClassPaths = await this.generateOutClassPath(pickSteps, rootNodes, outputPath); - } catch (e) { - return reject(e); - } - const outputFileName = join(outputPath, basename(outputPath) + ".jar"); - progress.report({ increment: 30, message: "Exporting jar..." }); - const exportResult = await Jdtls.exportJar(basename(description), outClassPaths, outputFileName); - if (exportResult === true) { - resolve(outputFileName); - } else { - reject(); - } - }); - } - - private static resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } - progress.report({ increment: 10, message: "Resolving main classes..." }); - if (this.mainMethods === undefined || this.mainMethods.length === 0) { - return resolve(""); - } - progress.report({ increment: 30, message: "Determining entry main class..." }); - const pickNodes: QuickPickNode[] = []; - for (const mainMethod of this.mainMethods) { - if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { - pickNodes.push(new QuickPickNode(this.getName(mainMethod), mainMethod.name)); - } - } - if (pickNodes.length === 0) { - return resolve(""); - } else { - const pickBox = window.createQuickPick(); - pickNodes.push(new QuickPickNode("No main class", "")); - pickBox.items = pickNodes; - pickBox.title = "Export Jar - Determine entry main class"; - pickBox.placeholder = "Select the entry main class..."; - pickBox.ignoreFocusOut = true; - pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - pickSteps.push(SOLVE_MAINMETHOD); - resolve(pickBox.selectedItems[0].description); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - } - }); - } - - private static failMessage(message: string) { - window.showInformationMessage(message, new Option(false), new Message("Done", true)); - } - - private static successMessage(outputFileName: string) { - let openInExplorer: Message; - if (platform() === "win32") { - openInExplorer = new Message("Reveal in File Explorer"); - } else if (platform() === "darwin") { - openInExplorer = new Message("Reveal in Finder"); - } else { - openInExplorer = new Message("Open Containing Folder"); - } - window.showInformationMessage("Successfully exported jar to" + EOL + outputFileName, - new Option(false), openInExplorer, new Message("Done", true)).then((messageResult) => { - if (messageResult === openInExplorer) { - commands.executeCommand("revealFileInOS", Uri.file(outputFileName)); - } - }); - } - - private static async generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { - const extension: Extension | undefined = extensions.getExtension("redhat.java"); - const extensionApi: any = await extension?.activate(); - const outClassPaths: string[] = []; - const setUris: Set = new Set(); - const pickDependencies: QuickPickNode[] = []; - const pickedDependencies: QuickPickNode[] = []; - for (const rootNode of rootNodes) { - const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - this.generateDependencies(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); - this.generateDependencies(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); - const modulePathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - this.generateDependencies(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); - this.generateDependencies(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); - } - const pickBox = window.createQuickPick(); - pickDependencies.sort((node1, node2) => { - if (node1.description !== node2.description) { - return node1.description.localeCompare(node2.description); - } - if (node1.type !== node2.type) { - return node2.type.localeCompare(node1.type); - } - return node1.label.localeCompare(node2.label); - }); - pickBox.items = pickDependencies; - pickDependencies.forEach((pickDependency) => { - if (pickDependency.picked) { - pickedDependencies.push(pickDependency); - } - }); - pickBox.selectedItems = pickedDependencies; - pickBox.title = "Export Jar - Determine elements"; - pickBox.placeholder = "Select the elements..."; - pickBox.canSelectMany = true; - pickBox.ignoreFocusOut = true; - pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - pickBox.selectedItems.forEach((item) => { - outClassPaths.push(item.uri); - }); - resolve(outClassPaths); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - }); - } - - private static generateDependencies(paths: string[], setUris: Set, pickDependencies: QuickPickNode[], - projectPath: string, isRuntime: boolean) { - paths.forEach((classpath: string) => { - const extName = extname(classpath); - const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); - const description = (isRuntime) ? "Runtime" : "Test"; - const type = (extName === ".jar") ? "external" : "internal"; - if (!setUris.has(classpath)) { - setUris.add(classpath); - pickDependencies.push(new QuickPickNode(baseName, description, classpath, type, isRuntime)); - } - }); - } - private static getName(data: MainMethodInfo) { - const point = data.name.lastIndexOf("."); - if (point === -1) { - return data.name; - } else { - return data.name.substring(point + 1); - } - } - -} - -class Option implements MessageOptions { - constructor(public modal?: boolean) { - } -} - -class Message implements MessageItem { - constructor(public title: string, public isCloseAffordance?: boolean) { - } -} - -class ClasspathResult { - public projectRoot: string; - public classpaths: string[]; - public modulepaths: string[]; -} - -export class MainMethodInfo { - public name: string; - public path: string; -} - -class InputFlowAction { - public static back = new InputFlowAction(); -} diff --git a/src/views/exportJarFileUtil.ts b/src/views/exportJarFileUtil.ts new file mode 100644 index 00000000..a4e58d63 --- /dev/null +++ b/src/views/exportJarFileUtil.ts @@ -0,0 +1,322 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { EOL, platform } from "os"; +import { basename, extname, join } from "path"; +import { CancellationToken, commands, Extension, extensions, ProgressLocation, + QuickInputButtons, Uri, window, workspace, WorkspaceFolder } from "vscode"; +import { isStandardServerReady } from "../extension"; +import { Jdtls } from "../java/jdtls"; +import { INodeData } from "../java/nodeData"; +import { buildWorkspace } from "./build"; +import { JarQuickPickItem } from "./jarQuickPickItem"; +import { WorkspaceNode } from "./workspaceNode"; + +const SOLVE_PROJECT = "solve project"; +const SOLVE_MAINMETHOD = "solve mainmethod"; +const GENERATE_JAR = "generate jar"; +const FINISH = "finish"; + +let mainMethods: MainMethodInfo[]; + +export async function createJarFile(node?: INodeData) { + if (!isStandardServerReady()) { + return; + } + window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar... ", + cancellable: true, + }, (progress, token): Promise => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); + }); + progress.report({ increment: 10, message: "Building workspace..." }); + if (await buildWorkspace() === false) { + return reject(); + } + mainMethods = await Jdtls.getMainMethod(); + const pickSteps: string[] = []; + let step: string = SOLVE_PROJECT; + let rootNodes: INodeData[] = []; + let projectFolder: WorkspaceFolder; + let pickResult: string; + let outputFileName: string; + while (step !== FINISH) { + try { + switch (step) { + case SOLVE_PROJECT: { + projectFolder = await resolveProject(progress, token, pickSteps, node); + rootNodes = await Jdtls.getProjects(projectFolder.uri.toString()); + step = SOLVE_MAINMETHOD; + break; + } + case SOLVE_MAINMETHOD: { + pickResult = await resolveMainMethod(progress, token, pickSteps, projectFolder.uri.fsPath); + step = GENERATE_JAR; + break; + } + case GENERATE_JAR: { + outputFileName = await generateJar(progress, token, pickSteps, rootNodes, pickResult, projectFolder.uri.fsPath); + resolve(outputFileName); + step = FINISH; + break; + } + } + } catch (err) { + if (err === InputFlowAction.back) { + step = pickSteps.pop(); + continue; + } else { + return reject(); + } + } + } + }); + }).then((message) => { successMessage(message); }); +} + +function resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { + return new Promise((resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } + const folders = workspace.workspaceFolders; + if (node instanceof WorkspaceNode) { + if (folders === undefined) { + return reject(); + } + folders.forEach((folder) => { + if (folder.uri.toString() === node.uri) { + return resolve(folder); + } + }); + return reject(); + } + let projectFolder: WorkspaceFolder; + if (folders && folders.length) { + if (folders.length === 1) { + return resolve(folders[0]); + } + progress.report({ increment: 10, message: "Selecting project..." }); + const pickNodes: JarQuickPickItem[] = []; + for (const folder of folders) { + pickNodes.push(new JarQuickPickItem(folder.name, folder.uri.fsPath, folder.uri.fsPath)); + } + const pickBox = window.createQuickPick(); + pickBox.items = pickNodes; + pickBox.title = "Export Jar - Determine project"; + pickBox.placeholder = "Select the project..."; + pickBox.ignoreFocusOut = true; + pickBox.onDidAccept(() => { + pickSteps.push(SOLVE_PROJECT); + folders.forEach((folder) => { + if (folder.uri.fsPath === pickBox.selectedItems[0].uri) { + projectFolder = folder; + } + }); + resolve(projectFolder); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + } else { + failMessage("No project found"); + return reject(); + } + }); +} + +function generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], + description: string, outputPath: string): Promise { + return new Promise(async (resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } else if (rootNodes === undefined) { + failMessage("No module found in this project"); + return reject(); + } + progress.report({ increment: 10, message: "Resolving classpaths..." }); + let outClassPaths: string[]; + try { + outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); + } catch (e) { + return reject(e); + } + const outputFileName = join(outputPath, basename(outputPath) + ".jar"); + progress.report({ increment: 30, message: "Generating jar..." }); + const exportResult = await Jdtls.exportJar(basename(description), outClassPaths, outputFileName); + if (exportResult === true) { + resolve(outputFileName); + } else { + reject(); + } + }); +} + +function resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { + return new Promise(async (resolve, reject) => { + if (token.isCancellationRequested) { + return reject(); + } + progress.report({ increment: 10, message: "Resolving main classes..." }); + if (mainMethods === undefined || mainMethods.length === 0) { + return resolve(""); + } + progress.report({ increment: 30, message: "Determining main class..." }); + const pickNodes: JarQuickPickItem[] = []; + for (const mainMethod of mainMethods) { + if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { + pickNodes.push(new JarQuickPickItem(getName(mainMethod), mainMethod.name)); + } + } + if (pickNodes.length === 0) { + return resolve(""); + } else { + const pickBox = window.createQuickPick(); + pickNodes.push(new JarQuickPickItem("No main class", "")); + pickBox.items = pickNodes; + pickBox.title = "Export Jar - Determine main class"; + pickBox.placeholder = "Select the main class..."; + pickBox.ignoreFocusOut = true; + pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + pickSteps.push(SOLVE_MAINMETHOD); + resolve(pickBox.selectedItems[0].description); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + } + }); +} + +function failMessage(message: string) { + window.showInformationMessage(message, "Done"); +} + +function successMessage(outputFileName: string) { + let openInExplorer: string; + if (platform() === "win32") { + openInExplorer = "Reveal in File Explorer"; + } else if (platform() === "darwin") { + openInExplorer = "Reveal in Finder"; + } else { + openInExplorer = "Open Containing Folder"; + } + window.showInformationMessage("Successfully exported jar to" + EOL + outputFileName, + openInExplorer, "Done").then((messageResult) => { + if (messageResult === openInExplorer) { + commands.executeCommand("revealFileInOS", Uri.file(outputFileName)); + } + }); +} + +async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { + return new Promise(async (resolve, reject) => { + const extension: Extension | undefined = extensions.getExtension("redhat.java"); + const extensionApi: any = await extension?.activate(); + const outClassPaths: string[] = []; + const setUris: Set = new Set(); + const pickDependencies: JarQuickPickItem[] = []; + const pickedDependencies: JarQuickPickItem[] = []; + for (const rootNode of rootNodes) { + const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + generateDependencies(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); + generateDependencies(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); + const modulePathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + generateDependencies(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); + generateDependencies(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); + } + const pickBox = window.createQuickPick(); + pickDependencies.sort((node1, node2) => { + if (node1.description !== node2.description) { + return node1.description.localeCompare(node2.description); + } + if (node1.type !== node2.type) { + return node2.type.localeCompare(node1.type); + } + return node1.label.localeCompare(node2.label); + }); + pickBox.items = pickDependencies; + pickDependencies.forEach((pickDependency) => { + if (pickDependency.picked) { + pickedDependencies.push(pickDependency); + } + }); + pickBox.selectedItems = pickedDependencies; + pickBox.title = "Export Jar - Determine elements"; + pickBox.placeholder = "Select the elements..."; + pickBox.canSelectMany = true; + pickBox.ignoreFocusOut = true; + pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + pickBox.selectedItems.forEach((item) => { + outClassPaths.push(item.uri); + }); + resolve(outClassPaths); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + }); +} + +function generateDependencies(paths: string[], setUris: Set, pickDependencies: JarQuickPickItem[], + projectPath: string, isRuntime: boolean) { + paths.forEach((classpath: string) => { + const extName = extname(classpath); + const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); + const description = (isRuntime) ? "Runtime" : "Test"; + const type = (extName === ".jar") ? "external" : "internal"; + if (!setUris.has(classpath)) { + setUris.add(classpath); + pickDependencies.push(new JarQuickPickItem(baseName, description, classpath, type, isRuntime)); + } + }); +} +function getName(data: MainMethodInfo) { + const point = data.name.lastIndexOf("."); + if (point === -1) { + return data.name; + } else { + return data.name.substring(point + 1); + } +} + +class ClasspathResult { + public projectRoot: string; + public classpaths: string[]; + public modulepaths: string[]; +} + +export class MainMethodInfo { + public name: string; + public path: string; +} + +class InputFlowAction { + public static back = new InputFlowAction(); +} diff --git a/src/views/quickPickNode.ts b/src/views/jarQuickPickItem.ts similarity index 90% rename from src/views/quickPickNode.ts rename to src/views/jarQuickPickItem.ts index c18dae43..b71ac184 100644 --- a/src/views/quickPickNode.ts +++ b/src/views/jarQuickPickItem.ts @@ -3,7 +3,7 @@ import { QuickPickItem } from "vscode"; -export class QuickPickNode implements QuickPickItem { +export class JarQuickPickItem implements QuickPickItem { public label: string; public description: string; From b9c673ad32e8635919fa76390d162e9e1f3e0ad1 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 20 Jul 2020 16:05:41 +0800 Subject: [PATCH 15/44] Error handing --- src/java/jdtls.ts | 2 +- src/views/exportJarFileUtil.ts | 70 +++++++++++++++------------------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index 3ed23af4..eb974051 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,7 +3,7 @@ import { commands } from "vscode"; import { Commands } from "../commands"; -import { MainMethodInfo } from "../views/exportJarFile"; +import { MainMethodInfo } from "../views/exportJarFileUtil"; import { INodeData } from "./nodeData"; export namespace Jdtls { diff --git a/src/views/exportJarFileUtil.ts b/src/views/exportJarFileUtil.ts index a4e58d63..eff1cecb 100644 --- a/src/views/exportJarFileUtil.ts +++ b/src/views/exportJarFileUtil.ts @@ -30,7 +30,7 @@ export async function createJarFile(node?: INodeData) { }, (progress, token): Promise => { return new Promise(async (resolve, reject) => { token.onCancellationRequested(() => { - return reject(); + return reject("User Cancelled."); }); progress.report({ increment: 10, message: "Building workspace..." }); if (await buildWorkspace() === false) { @@ -40,7 +40,8 @@ export async function createJarFile(node?: INodeData) { const pickSteps: string[] = []; let step: string = SOLVE_PROJECT; let rootNodes: INodeData[] = []; - let projectFolder: WorkspaceFolder; + let projectFolder: string; + let projectUri: Uri; let pickResult: string; let outputFileName: string; while (step !== FINISH) { @@ -48,17 +49,18 @@ export async function createJarFile(node?: INodeData) { switch (step) { case SOLVE_PROJECT: { projectFolder = await resolveProject(progress, token, pickSteps, node); - rootNodes = await Jdtls.getProjects(projectFolder.uri.toString()); + projectUri = Uri.parse(projectFolder); + rootNodes = await Jdtls.getProjects(projectUri.toString()); step = SOLVE_MAINMETHOD; break; } case SOLVE_MAINMETHOD: { - pickResult = await resolveMainMethod(progress, token, pickSteps, projectFolder.uri.fsPath); + pickResult = await resolveMainMethod(progress, token, pickSteps, projectUri.fsPath); step = GENERATE_JAR; break; } case GENERATE_JAR: { - outputFileName = await generateJar(progress, token, pickSteps, rootNodes, pickResult, projectFolder.uri.fsPath); + outputFileName = await generateJar(progress, token, pickSteps, rootNodes, pickResult, projectUri.fsPath); resolve(outputFileName); step = FINISH; break; @@ -69,40 +71,31 @@ export async function createJarFile(node?: INodeData) { step = pickSteps.pop(); continue; } else { - return reject(); + return reject(err); } } } }); - }).then((message) => { successMessage(message); }); + }).then((message) => { successMessage(message); }, (err) => { failMessage(err); }); } -function resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { - return new Promise((resolve, reject) => { +function resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { + return new Promise((resolve, reject) => { if (token.isCancellationRequested) { - return reject(); + return reject("User Cancelled."); } - const folders = workspace.workspaceFolders; if (node instanceof WorkspaceNode) { - if (folders === undefined) { - return reject(); - } - folders.forEach((folder) => { - if (folder.uri.toString() === node.uri) { - return resolve(folder); - } - }); - return reject(); + return resolve(node.uri); } - let projectFolder: WorkspaceFolder; + const folders = workspace.workspaceFolders; if (folders && folders.length) { if (folders.length === 1) { - return resolve(folders[0]); + return resolve(folders[0].uri.toString()); } progress.report({ increment: 10, message: "Selecting project..." }); const pickNodes: JarQuickPickItem[] = []; for (const folder of folders) { - pickNodes.push(new JarQuickPickItem(folder.name, folder.uri.fsPath, folder.uri.fsPath)); + pickNodes.push(new JarQuickPickItem(folder.name, folder.uri.fsPath, folder.uri.toString())); } const pickBox = window.createQuickPick(); pickBox.items = pickNodes; @@ -111,12 +104,7 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], pickBox.ignoreFocusOut = true; pickBox.onDidAccept(() => { pickSteps.push(SOLVE_PROJECT); - folders.forEach((folder) => { - if (folder.uri.fsPath === pickBox.selectedItems[0].uri) { - projectFolder = folder; - } - }); - resolve(projectFolder); + resolve(pickBox.selectedItems[0].uri); pickBox.dispose(); }); pickBox.onDidHide(() => { @@ -125,20 +113,18 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], }); pickBox.show(); } else { - failMessage("No project found"); - return reject(); + return reject("No workspace folder found."); } }); } function generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], description: string, outputPath: string): Promise { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { - return reject(); + return reject("User Cancelled."); } else if (rootNodes === undefined) { - failMessage("No module found in this project"); - return reject(); + return reject("No project found."); } progress.report({ increment: 10, message: "Resolving classpaths..." }); let outClassPaths: string[]; @@ -153,15 +139,15 @@ function generateJar(progress, token: CancellationToken, pickSteps: string[], ro if (exportResult === true) { resolve(outputFileName); } else { - reject(); + reject("Export jar failed."); } }); } function resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { - return reject(); + return reject("User Cancelled."); } progress.report({ increment: 10, message: "Resolving main classes..." }); if (mainMethods === undefined || mainMethods.length === 0) { @@ -226,7 +212,7 @@ function successMessage(outputFileName: string) { } async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { const extension: Extension | undefined = extensions.getExtension("redhat.java"); const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; @@ -241,6 +227,12 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], generateDependencies(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); generateDependencies(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); } + if (pickDependencies.length === 0) { + return reject("No class path found."); + } else if (pickDependencies.length === 1) { + outClassPaths.push(pickDependencies[0].uri); + return resolve(outClassPaths); + } const pickBox = window.createQuickPick(); pickDependencies.sort((node1, node2) => { if (node1.description !== node2.description) { From e502fb9cf6341080b9c84d3a064fa6327b40c41f Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 21 Jul 2020 23:44:43 +0800 Subject: [PATCH 16/44] Apply changes --- .../internal/jarpackager/JarPackageUtil.java | 612 +++++++++--------- package.nls.json | 2 +- package.nls.zh.json | 2 +- src/commands.ts | 8 - src/java/jdtls.ts | 21 +- src/languageServerPlugin.ts | 14 - src/logger.ts | 61 -- src/utility.ts | 27 +- src/views/IJarQuickPickItem.ts | 12 + src/views/build.ts | 44 +- src/views/exportJarFileUtil.ts | 76 ++- src/views/jarQuickPickItem.ts | 22 - 12 files changed, 404 insertions(+), 497 deletions(-) delete mode 100644 src/languageServerPlugin.ts delete mode 100644 src/logger.ts create mode 100644 src/views/IJarQuickPickItem.ts delete mode 100644 src/views/jarQuickPickItem.ts diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java index e34c4be2..a3ed3c5d 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/org/eclipse/jdt/internal/jarpackager/JarPackageUtil.java @@ -41,327 +41,317 @@ public class JarPackageUtil { - private static final int INTERNAL_ERROR= 10001; - - /** - * Write the given entry describing the given content to the current archive. - * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 - * - * @param entry the entry to write - * @param content the content to write - * @param fJarOutputStream the destination JarOutputStream - * - * @throws IOException If an I/O error occurred - * - * @since 1.14 - * - */ - private static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { - byte[] readBuffer = new byte[4096]; - try { - fJarOutputStream.putNextEntry(entry); - int count; - while ((count = content.read(readBuffer, 0, readBuffer.length)) != -1) - fJarOutputStream.write(readBuffer, 0, count); - } finally { - if (content != null) - content.close(); + private static final int INTERNAL_ERROR= 10001; + + /** + * Write the given entry describing the given content to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param entry the entry to write + * @param content the content to write + * @param fJarOutputStream the destination JarOutputStream + * + * @throws IOException If an I/O error occurred + * + * @since 1.14 + * + */ + private static void addEntry(JarEntry entry, InputStream content, JarOutputStream fJarOutputStream) throws IOException { + byte[] readBuffer = new byte[4096]; + try { + fJarOutputStream.putNextEntry(entry); + int count; + while ((count = content.read(readBuffer, 0, readBuffer.length)) != -1) { + fJarOutputStream.write(readBuffer, 0, count); + } + } finally { + if (content != null) { + content.close(); + } + /* + * Commented out because some JREs throw an NPE if a stream + * is closed twice. This works because + * a) putNextEntry closes the previous entry + * b) closing the stream closes the last entry + */ + } + } - /* - * Commented out because some JREs throw an NPE if a stream - * is closed twice. This works because - * a) putNextEntry closes the previous entry - * b) closing the stream closes the last entry - */ - } - } + /** + * Write the contents of the given zipfile to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder + * + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param progressMonitor the progressMonitor + * + * @return the MultiStatus saving the warnings during the process + * + * @since 1.14 + * + */ + public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntriesIncluded, + boolean isCompressed, JarOutputStream fJarOutputStream, + Set fDirectories, IProgressMonitor progressMonitor) { + MultiStatus fStatus = new MultiStatus(JdtlsExtActivator.PLUGIN_ID, IStatus.OK, ""); //$NON-NLS-1$ + Enumeration jarEntriesEnum = zipFile.entries(); + File zipFile1 = new File(zipFile.getName()); + try { + String zipFileCanonical = zipFile1.getCanonicalPath(); + while (jarEntriesEnum.hasMoreElements()) { + ZipEntry zipEntry = jarEntriesEnum.nextElement(); + if (!zipEntry.isDirectory()) { + String entryName = zipEntry.getName(); + File zipEntryFile = new File(zipFile1, entryName); + String zipEntryCanonical = zipEntryFile.getCanonicalPath(); + if (zipEntryCanonical.startsWith(zipFileCanonical + File.separator)) { + addFile(entryName, zipEntry, zipFile, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories, fStatus); + } else { + addWarning("Invalid path" + entryName, null, fStatus); //$NON-NLS-1$ + } + } + progressMonitor.worked(1); + if (progressMonitor.isCanceled()) { + throw new OperationCanceledException(); + } + } + } catch (IOException e) { + addWarning("ZipFile error" + zipFile.getName(), null, fStatus); //$NON-NLS-1$ + e.printStackTrace(); + } + return fStatus; + } - /** - * Write the contents of the given zipfile to the JarOutputStream. - * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder - * - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded the directory entries are included - * @param isCompressed the jar is compressed - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * @param progressMonitor the progressMonitor - * - * @return the MultiStatus saving the warnings during the process - * - * @since 1.14 - * - */ - public static MultiStatus writeArchive(ZipFile zipFile, boolean areDirectoryEntriesIncluded, - boolean isCompressed, JarOutputStream fJarOutputStream, - Set fDirectories, IProgressMonitor progressMonitor) { - MultiStatus fStatus = new MultiStatus(JdtlsExtActivator.PLUGIN_ID, IStatus.OK, ""); //$NON-NLS-1$ - Enumeration jarEntriesEnum = zipFile.entries(); - File zipFile1 = new File(zipFile.getName()); - try { - String zipFileCanonical = zipFile1.getCanonicalPath(); + /** + * Write the entry to the destinationPath of the given JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder + * + * @param destinationPath the destinationPath in the jar file + * @param jarEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * @param fStatus the MultiStatus saving the warnings during the process + * + * @since 1.14 + * + */ + private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile zipFile, + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus) { + // Handle META-INF/MANIFEST.MF + if (destinationPath.equalsIgnoreCase("META-INF/MANIFEST.MF") //$NON-NLS-1$ + || (destinationPath.startsWith("META-INF/") && destinationPath.endsWith(".SF"))) { //$NON-NLS-1$//$NON-NLS-2$ + return; + } + try { + addZipEntry(jarEntry, zipFile, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); + } catch (IOException ex) { + if (ex instanceof ZipException && ex.getMessage() != null && ex.getMessage().startsWith("duplicate entry:")) {//$NON-NLS-1$ + // ignore duplicates in META-INF (*.SF, *.RSA) + if (!destinationPath.startsWith("META-INF/")) { //$NON-NLS-1$ + addWarning(ex.getMessage(), ex, fStatus); + } + } + } + } - while (jarEntriesEnum.hasMoreElements()) { - ZipEntry zipEntry = jarEntriesEnum.nextElement(); - if (!zipEntry.isDirectory()) { - String entryName = zipEntry.getName(); - File zipEntryFile = new File(zipFile1, entryName); - String zipEntryCanonical = zipEntryFile.getCanonicalPath(); - if (zipEntryCanonical.startsWith(zipFileCanonical + File.separator)) { - addFile(entryName, zipEntry, zipFile, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories, fStatus); - } - else { - addWarning("Invalid path" + entryName, null, fStatus); //$NON-NLS-1$ - } - } - progressMonitor.worked(1); - if (progressMonitor.isCanceled()) - throw new OperationCanceledException(); - } - } catch (IOException e) { - addWarning("ZipFile error" + zipFile.getName(), null, fStatus); //$NON-NLS-1$ - e.printStackTrace(); - } - return fStatus; - } + /** + * Write the entry to the destinationPath of the given JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param zipEntry the jar entry to write + * @param zipFile the zipfile to extract + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException If an I/O error occurred + * + * @since 1.14 + * + */ + private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, + boolean areDirectoryEntriesIncluded, boolean isCompressed, + JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + if (areDirectoryEntriesIncluded) { + addDirectories(path, fJarOutputStream, fDirectories); + } + JarEntry newEntry = new JarEntry(path.replace(File.separatorChar, '/')); + if (isCompressed) { + newEntry.setMethod(ZipEntry.DEFLATED); + // Entry is filled automatically. + } else { + newEntry.setMethod(ZipEntry.STORED); + newEntry.setSize(zipEntry.getSize()); + newEntry.setCrc(zipEntry.getCrc()); + } + long lastModified = System.currentTimeMillis(); + // Set modification time + newEntry.setTime(lastModified); + addEntry(newEntry, zipFile.getInputStream(zipEntry), fJarOutputStream); + } - /** - * Write the entry to the destinationPath of the given JarOutputStream. - * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.UnpackFatJarBuilder - * - * @param destinationPath the destinationPath in the jar file - * @param jarEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param areDirectoryEntriesIncluded the directory entries are included - * @param isCompressed the jar is compressed - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * @param fStatus the MultiStatus saving the warnings during the process - * - * @since 1.14 - * - */ - private static void addFile(String destinationPath, ZipEntry jarEntry, ZipFile zipFile, - boolean areDirectoryEntriesIncluded, boolean isCompressed, - JarOutputStream fJarOutputStream, Set fDirectories, MultiStatus fStatus) { - // Handle META-INF/MANIFEST.MF - if (destinationPath.equalsIgnoreCase("META-INF/MANIFEST.MF") //$NON-NLS-1$ - || (destinationPath.startsWith("META-INF/") && destinationPath.endsWith(".SF"))) { //$NON-NLS-1$//$NON-NLS-2$ - return; - } - try { - addZipEntry(jarEntry, zipFile, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); - } catch (IOException ex) { - if (ex instanceof ZipException && ex.getMessage() != null && ex.getMessage().startsWith("duplicate entry:")) {//$NON-NLS-1$ - // ignore duplicates in META-INF (*.SF, *.RSA) - if (!destinationPath.startsWith("META-INF/")) { //$NON-NLS-1$ - addWarning(ex.getMessage(), ex, fStatus); - } - } - } - } + /** + * Creates the directory entries for the given path and writes it to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param destPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + */ + private static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + String path = destPath.replace(File.separatorChar, '/'); + int lastSlash = path.lastIndexOf('/'); + List directories = new ArrayList<>(2); + while (lastSlash != -1) { + path= path.substring(0, lastSlash + 1); + if (!fDirectories.add(path)) { + break; + } + JarEntry newEntry = new JarEntry(path); + newEntry.setMethod(ZipEntry.STORED); + newEntry.setSize(0); + newEntry.setCrc(0); + newEntry.setTime(System.currentTimeMillis()); + directories.add(newEntry); + lastSlash= path.lastIndexOf('/', lastSlash - 1); + } + for (int i = directories.size() - 1; i >= 0; --i) { + fJarOutputStream.putNextEntry(directories.get(i)); + } + } - /** - * Write the entry to the destinationPath of the given JarOutputStream. - * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 - * - * @param zipEntry the jar entry to write - * @param zipFile the zipfile to extract - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded the directory entries are included - * @param isCompressed the jar is compressed - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * - * @throws IOException If an I/O error occurred - * - * @since 1.14 - * - */ - private static void addZipEntry(ZipEntry zipEntry, ZipFile zipFile, String path, - boolean areDirectoryEntriesIncluded, boolean isCompressed, - JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { - if (areDirectoryEntriesIncluded) - addDirectories(path, fJarOutputStream, fDirectories); - - JarEntry newEntry = new JarEntry(path.replace(File.separatorChar, '/')); - - if (isCompressed) - newEntry.setMethod(ZipEntry.DEFLATED); - // Entry is filled automatically. - else { - newEntry.setMethod(ZipEntry.STORED); - newEntry.setSize(zipEntry.getSize()); - newEntry.setCrc(zipEntry.getCrc()); - } - - long lastModified = System.currentTimeMillis(); - - // Set modification time - newEntry.setTime(lastModified); - - addEntry(newEntry, zipFile.getInputStream(zipEntry), fJarOutputStream); - } - - /** - * Creates the directory entries for the given path and writes it to the current archive. - * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 - * - * @param destPath the path to add - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * - * @throws IOException if an I/O error has occurred - * - * @since 1.14 - */ - private static void addDirectories(String destPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { - String path = destPath.replace(File.separatorChar, '/'); - int lastSlash = path.lastIndexOf('/'); - List directories = new ArrayList<>(2); - while (lastSlash != -1) { - path= path.substring(0, lastSlash + 1); - if (!fDirectories.add(path)) - break; - - JarEntry newEntry = new JarEntry(path); - newEntry.setMethod(ZipEntry.STORED); - newEntry.setSize(0); - newEntry.setCrc(0); - newEntry.setTime(System.currentTimeMillis()); - directories.add(newEntry); - - lastSlash= path.lastIndexOf('/', lastSlash - 1); - } - - for (int i = directories.size() - 1; i >= 0; --i) { - fJarOutputStream.putNextEntry(directories.get(i)); - } - } - - /** - * Write the single file to the JarOutputStream. - * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 - * - * @param file the file to write - * @param destinationPath the destinationPath in the jar file - * @param areDirectoryEntriesIncluded the directory entries are included - * @param isCompressed the jar is compressed - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * - * @throws CoreException if an error has occurred - * - * @since 1.14 - * - */ - public static void writeFile(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, + /** + * Write the single file to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param file the file to write + * @param destinationPath the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws CoreException if an error has occurred + * + * @since 1.14 + * + */ + public static void writeFile(File file, IPath destinationPath, boolean areDirectoryEntriesIncluded, boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories) throws CoreException { - try { - addFile(file, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); - } catch (IOException ex) { - throw new CoreException(new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, ex.getLocalizedMessage(), ex)); - } - } + try { + addFile(file, destinationPath, areDirectoryEntriesIncluded, isCompressed, fJarOutputStream, fDirectories); + } catch (IOException ex) { + throw new CoreException(new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, ex.getLocalizedMessage(), ex)); + } + } - /** - * Add the single file to the JarOutputStream. - * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 - * - * @param file the file to write - * @param path the destinationPath in the jar file - * @param areDirectoryEntriesIncluded the directory entries are included - * @param isCompressed the jar is compressed - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * - * @throws IOException if an I/O error has occurred - * - * @since 1.14 - * - */ - private static void addFile(File file, IPath path, boolean areDirectoryEntriesIncluded, + /** + * Add the single file to the JarOutputStream. + * Extracted from org.eclipse.jdt.internal.ui.jarpackagerfat.JarWriter4 + * + * @param file the file to write + * @param path the destinationPath in the jar file + * @param areDirectoryEntriesIncluded the directory entries are included + * @param isCompressed the jar is compressed + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + * + */ + private static void addFile(File file, IPath path, boolean areDirectoryEntriesIncluded, boolean isCompressed, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { - if (areDirectoryEntriesIncluded) - addDirectories(path, fJarOutputStream, fDirectories); - - JarEntry newEntry = new JarEntry(path.toString().replace(File.separatorChar, '/')); - - if (isCompressed) - newEntry.setMethod(ZipEntry.DEFLATED); - // Entry is filled automatically. - else { - newEntry.setMethod(ZipEntry.STORED); - calculateCrcAndSize(newEntry, new FileInputStream(file), new byte[4096]); - } + if (areDirectoryEntriesIncluded) { + addDirectories(path, fJarOutputStream, fDirectories); + } + JarEntry newEntry = new JarEntry(path.toString().replace(File.separatorChar, '/')); + if (isCompressed) { + newEntry.setMethod(ZipEntry.DEFLATED); + // Entry is filled automatically. + } else { + newEntry.setMethod(ZipEntry.STORED); + calculateCrcAndSize(newEntry, new FileInputStream(file), new byte[4096]); + } + newEntry.setTime(file.lastModified()); + addEntry(newEntry, new FileInputStream(file), fJarOutputStream); + } - newEntry.setTime(file.lastModified()); - addEntry(newEntry, new FileInputStream(file), fJarOutputStream); - } + /** + * Creates the directory entries for the given path and writes it to the current archive. + * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 + * + * @param destinationPath the path to add + * @param fJarOutputStream the destination JarOutputStream + * @param fDirectories the temporary set saves existing directories + * + * @throws IOException if an I/O error has occurred + * + * @since 1.14 + */ + private static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { + addDirectories(destinationPath.toString(), fJarOutputStream, fDirectories); + } - /** - * Creates the directory entries for the given path and writes it to the current archive. - * Extracted from org.eclipse.jdt.ui.jarpackager.JarWriter3 - * - * @param destinationPath the path to add - * @param fJarOutputStream the destination JarOutputStream - * @param fDirectories the temporary set saves existing directories - * - * @throws IOException if an I/O error has occurred - * - * @since 1.14 - */ - private static void addDirectories(IPath destinationPath, JarOutputStream fJarOutputStream, Set fDirectories) throws IOException { - addDirectories(destinationPath.toString(), fJarOutputStream, fDirectories); - } + /** + * Calculates the crc and size of the resource and updates the entry. + * Extracted from org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil + * + * @param entry the jar entry to update + * @param stream the input stream + * @param buffer a shared buffer to store temporary data + * + * @throws IOException if an input/output error occurs + * + * @since 1.14 + */ + private static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { + int size = 0; + final CRC32 crc = new CRC32(); + int count; + try { + while ((count = stream.read(buffer, 0, buffer.length)) != -1) { + crc.update(buffer, 0, count); + size += count; + } + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException exception) { + // Do nothing + } + } + } + entry.setSize(size); + entry.setCrc(crc.getValue()); + } - /** - * Calculates the crc and size of the resource and updates the entry. - * Extracted from org.eclipse.jdt.internal.ui.jarpackager.JarPackagerUtil - * - * @param entry the jar entry to update - * @param stream the input stream - * @param buffer a shared buffer to store temporary data - * - * @throws IOException if an input/output error occurs - * - * @since 1.14 - */ - private static void calculateCrcAndSize(final ZipEntry entry, final InputStream stream, final byte[] buffer) throws IOException { - int size = 0; - final CRC32 crc = new CRC32(); - int count; - try { - while ((count = stream.read(buffer, 0, buffer.length)) != -1) { - crc.update(buffer, 0, count); - size += count; - } - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException exception) { - // Do nothing - } - } - } - entry.setSize(size); - entry.setCrc(crc.getValue()); - } - - /** - * add a warning message into the MultiStatus. - * - * @param message the message to add - * @param error the reason of the message - * @param fStatus the MultiStatus to write - * - * @since 1.14 - */ - private final static void addWarning(String message, Throwable error, MultiStatus fStatus) { - fStatus.add(new Status(IStatus.WARNING, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, message, error)); - } + /** + * add a warning message into the MultiStatus. + * + * @param message the message to add + * @param error the reason of the message + * @param fStatus the MultiStatus to write + * + * @since 1.14 + */ + private final static void addWarning(String message, Throwable error, MultiStatus fStatus) { + fStatus.add(new Status(IStatus.WARNING, JdtlsExtActivator.PLUGIN_ID, INTERNAL_ERROR, message, error)); + } } - - diff --git a/package.nls.json b/package.nls.json index 8c5fbdc1..100cfa37 100644 --- a/package.nls.json +++ b/package.nls.json @@ -11,7 +11,7 @@ "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.exportJar": "Export Jar", + "contributes.commands.java.view.package.exportJar": "Export Jar...", "contributes.commands.java.view.package.copyFilePath": "Copy Path", "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "configuration.java.dependency.title": "Java Dependency Configuration", diff --git a/package.nls.zh.json b/package.nls.zh.json index ed5f35b7..98d28ad7 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -11,7 +11,7 @@ "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.exportJar": "导出到 Jar 文件", + "contributes.commands.java.view.package.exportJar": "导出到 Jar 文件...", "contributes.commands.java.view.package.copyFilePath": "复制路径", "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", diff --git a/src/commands.ts b/src/commands.ts index 61eb519d..b08c55eb 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. import { commands } from "vscode"; -import { getJavaExtension, JavaExtensionNotEnabledError } from "./utility"; /** * Commonly used commands */ @@ -79,12 +78,5 @@ export function executeJavaLanguageServerCommand(...rest) { export async function executeJavaExtensionCommand(commandName: string, ...rest) { // TODO: need to handle error and trace telemetry - const javaExtension = getJavaExtension(); - if (!javaExtension) { - throw new JavaExtensionNotEnabledError(`Cannot execute command ${commandName}, VS Code Java Extension is not enabled.`); - } - if (!javaExtension.isActive) { - await javaExtension.activate(); - } return commands.executeCommand(commandName, ...rest); } diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index eb974051..006f1b4b 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -2,16 +2,16 @@ // Licensed under the MIT license. import { commands } from "vscode"; -import { Commands } from "../commands"; +import { Commands, executeJavaLanguageServerCommand, JAVA_RESOLVE_BUILD_FILES } from "../commands"; import { MainMethodInfo } from "../views/exportJarFileUtil"; import { INodeData } from "./nodeData"; export namespace Jdtls { - export function getProjects(params): Thenable { + export function getProjects(params: string): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_LIST, params); } - export function refreshLibraries(params): Thenable { + export function refreshLibraries(params: string): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_REFRESH_LIB_SERVER, params); } @@ -19,7 +19,7 @@ export namespace Jdtls { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_GETPACKAGEDATA, params); } - export function resolvePath(params): Thenable { + export function resolvePath(params: string): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params); } @@ -27,7 +27,18 @@ export namespace Jdtls { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINMETHOD); } - export function exportJar(mainMethod, elements, destination): Thenable { + export function exportJar(mainMethod: string, elements: string[], destination: string): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_EXPORTJAR, mainMethod, elements, destination); } } + +export enum CompileWorkspaceStatus { + Failed = 0, + Succeed = 1, + Witherror = 2, + Cancelled = 3, +} + +export function resolveBuildFiles(): Promise { + return >executeJavaLanguageServerCommand(JAVA_RESOLVE_BUILD_FILES); +} diff --git a/src/languageServerPlugin.ts b/src/languageServerPlugin.ts deleted file mode 100644 index e8025e60..00000000 --- a/src/languageServerPlugin.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -import * as commands from "./commands"; - -export enum CompileWorkspaceStatus { - Failed = 0, - Succeed = 1, - Witherror = 2, - Cancelled = 3, -} - -export function resolveBuildFiles(): Promise { - return >commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_BUILD_FILES); -} diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index f765b4b4..00000000 --- a/src/logger.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import * as fs from "fs"; -import * as vscode from "vscode"; -import TelemetryReporter from "vscode-extension-telemetry"; - -export enum Type { - EXCEPTION = "exception", - USAGEDATA = "usageData", - USAGEERROR = "usageError", - ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category. -} - -const SENSITIVE_PROPS = ["message", "stacktrace", "detailmessage"]; - -class Logger implements vscode.Disposable { - private reporter: TelemetryReporter = null; - - public initialize(context: vscode.ExtensionContext, firstParty?: boolean): void { - if (this.reporter) { - return; - } - - const extensionPackage = JSON.parse(fs.readFileSync(context.asAbsolutePath("./package.json"), "utf-8")); - if (extensionPackage) { - const packageInfo = { - name: extensionPackage.name, - version: extensionPackage.version, - aiKey: extensionPackage.aiKey, - }; - if (packageInfo.aiKey) { - this.reporter = new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey, firstParty); - } - } - } - - public log(type: Type, properties?: { [key: string]: string; }, measures?: { [key: string]: number; }): void { - if (!this.reporter) { - return; - } - - if (type === Type.EXCEPTION || type === Type.USAGEERROR) { - this.reporter.sendTelemetryErrorEvent(type, properties, measures, SENSITIVE_PROPS); - } else { - this.reporter.sendTelemetryEvent(type, properties, measures); - } - } - - public logMessage(type: Type, message: string): void { - this.log(type, { message }); - } - - public dispose() { - if (this.reporter) { - this.reporter.dispose(); - } - } -} - -export const logger = new Logger(); diff --git a/src/utility.ts b/src/utility.ts index 39df35aa..8775e8ac 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -1,11 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { commands, Extension, extensions, Uri, window, workspace, WorkspaceFolder } from "vscode"; +import { window, workspace, WorkspaceFolder } from "vscode"; import { setUserError } from "vscode-extension-telemetry-wrapper"; -import { logger, Type } from "./logger"; -const JAVA_EXTENSION_ID = "redhat.java"; -const TROUBLESHOOTING_LINK = "https://github.com/Microsoft/vscode-java-debug/blob/master/Troubleshooting.md"; export class Utility { @@ -26,17 +23,6 @@ export class Utility { } -export function getJavaExtension(): Extension { - return extensions.getExtension(JAVA_EXTENSION_ID); -} - -export class JavaExtensionNotEnabledError extends Error { - constructor(message) { - super(message); - setUserError(this); - } -} - export class UserError extends Error { public context: ITroubleshootingMessage; @@ -61,10 +47,9 @@ interface ITroubleshootingMessage extends ILoggingMessage { anchor?: string; } -export function openTroubleshootingPage(message: string, anchor: string) { - commands.executeCommand("vscode.open", Uri.parse(anchor ? `${TROUBLESHOOTING_LINK}#${anchor}` : TROUBLESHOOTING_LINK)); - logger.log(Type.USAGEDATA, { - troubleshooting: "yes", - troubleshootingMessage: message, - }); +export enum Type { + EXCEPTION = "exception", + USAGEDATA = "usageData", + USAGEERROR = "usageError", + ACTIVATEEXTENSION = "activateExtension", // TODO: Activation belongs to usage data, remove this category. } diff --git a/src/views/IJarQuickPickItem.ts b/src/views/IJarQuickPickItem.ts new file mode 100644 index 00000000..3cac9386 --- /dev/null +++ b/src/views/IJarQuickPickItem.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { QuickPickItem } from "vscode"; + +export interface IJarQuickPickItem extends QuickPickItem { + label: string; + description: string; + uri?: string; + type?: string; + picked?: boolean; +} diff --git a/src/views/build.ts b/src/views/build.ts index 86c020a7..12ca4dbc 100644 --- a/src/views/build.ts +++ b/src/views/build.ts @@ -1,18 +1,18 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import * as path from "path"; -import * as vscode from "vscode"; +import { basename } from "path"; +import { commands, DiagnosticSeverity, languages, Uri, window } from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; -import * as commands from "../commands"; -import * as lsPlugin from "../languageServerPlugin"; -import * as utility from "../utility"; +import { CompileWorkspaceStatus, resolveBuildFiles} from "../java/jdtls"; +import { executeJavaExtensionCommand, JAVA_BUILD_WORKSPACE } from "../commands"; +import { UserError } from "../utility"; export async function buildWorkspace(): Promise { const buildResult = await instrumentOperation("build", async (operationId: string) => { let error; try { - await commands.executeJavaExtensionCommand(commands.JAVA_BUILD_WORKSPACE, false); + await executeJavaExtensionCommand(JAVA_BUILD_WORKSPACE, false); } catch (err) { error = err; } @@ -31,17 +31,17 @@ export async function buildWorkspace(): Promise { async function handleBuildFailure(operationId: string, err: any): Promise { - const error: Error = new utility.UserError({ + const error: Error = new UserError({ message: "Build failed", }); setErrorCode(error, Number(err)); sendOperationError(operationId, "build", error); - if (err === lsPlugin.CompileWorkspaceStatus.Witherror || err === lsPlugin.CompileWorkspaceStatus.Failed) { + if (err === CompileWorkspaceStatus.Witherror || err === CompileWorkspaceStatus.Failed) { if (checkErrorsReportedByJavaExtension()) { - vscode.commands.executeCommand("workbench.actions.view.problems"); + commands.executeCommand("workbench.actions.view.problems"); } - const ans = await vscode.window.showErrorMessage("Build failed, do you want to continue?", + const ans = await window.showErrorMessage("Build failed, do you want to continue?", "Proceed", "Fix...", "Cancel"); sendInfo(operationId, { operationName: "build", @@ -58,11 +58,11 @@ async function handleBuildFailure(operationId: string, err: any): Promise diagnostic.severity === vscode.DiagnosticSeverity.Error).length) { + if (problem[1].filter((diagnostic) => diagnostic.severity === DiagnosticSeverity.Error).length) { return true; } } @@ -70,12 +70,10 @@ function checkErrorsReportedByJavaExtension(): boolean { return false; } -export const BUILD_FAILED = "build-failed-do-you-want-to-continue"; - async function showFixSuggestions(operationId: string) { let buildFiles = []; try { - buildFiles = await lsPlugin.resolveBuildFiles(); + buildFiles = await resolveBuildFiles(); } catch (error) { // do nothing } @@ -95,12 +93,8 @@ async function showFixSuggestions(operationId: string) { label: "Open log file", detail: "Open log file to view more details for the build errors", }); - pickitems.push({ - label: "Troubleshooting guide", - detail: "Find more detail about the troubleshooting steps", - }); - const ans = await vscode.window.showQuickPick(pickitems, { + const ans = await window.showQuickPick(pickitems, { placeHolder: "Please fix the errors in PROBLEMS first, then try the fix suggestions below.", }); sendInfo(operationId, { @@ -112,14 +106,12 @@ async function showFixSuggestions(operationId: string) { } if (ans.label === "Clean workspace cache") { - vscode.commands.executeCommand("java.clean.workspace"); + commands.executeCommand("java.clean.workspace"); } else if (ans.label === "Update project configuration") { for (const buildFile of buildFiles) { - await vscode.commands.executeCommand("java.projectConfiguration.update", vscode.Uri.parse(buildFile)); + await commands.executeCommand("java.projectConfiguration.update", Uri.parse(buildFile)); } } else if (ans.label === "Open log file") { - vscode.commands.executeCommand("java.open.serverLog"); - } else if (ans.label === "Troubleshooting guide") { - utility.openTroubleshootingPage("Build failed", BUILD_FAILED); + commands.executeCommand("java.open.serverLog"); } } diff --git a/src/views/exportJarFileUtil.ts b/src/views/exportJarFileUtil.ts index eff1cecb..b89e9804 100644 --- a/src/views/exportJarFileUtil.ts +++ b/src/views/exportJarFileUtil.ts @@ -9,13 +9,15 @@ import { isStandardServerReady } from "../extension"; import { Jdtls } from "../java/jdtls"; import { INodeData } from "../java/nodeData"; import { buildWorkspace } from "./build"; -import { JarQuickPickItem } from "./jarQuickPickItem"; +import { IJarQuickPickItem } from "./IJarQuickPickItem"; import { WorkspaceNode } from "./workspaceNode"; -const SOLVE_PROJECT = "solve project"; -const SOLVE_MAINMETHOD = "solve mainmethod"; -const GENERATE_JAR = "generate jar"; -const FINISH = "finish"; +enum ExportSteps{ + ResolveProject = "RESOLVEPROJECT", + ResolveMainMethod = "RESOLVEMAINMETHOD", + GenerateJar = "GENERATEJAR", + Finish = "FINISH" +} let mainMethods: MainMethodInfo[]; @@ -38,31 +40,31 @@ export async function createJarFile(node?: INodeData) { } mainMethods = await Jdtls.getMainMethod(); const pickSteps: string[] = []; - let step: string = SOLVE_PROJECT; + let step: string = ExportSteps.ResolveProject; let rootNodes: INodeData[] = []; let projectFolder: string; let projectUri: Uri; let pickResult: string; let outputFileName: string; - while (step !== FINISH) { + while (step !== ExportSteps.Finish) { try { switch (step) { - case SOLVE_PROJECT: { + case ExportSteps.ResolveProject: { projectFolder = await resolveProject(progress, token, pickSteps, node); projectUri = Uri.parse(projectFolder); rootNodes = await Jdtls.getProjects(projectUri.toString()); - step = SOLVE_MAINMETHOD; + step = ExportSteps.ResolveMainMethod; break; } - case SOLVE_MAINMETHOD: { + case ExportSteps.ResolveMainMethod: { pickResult = await resolveMainMethod(progress, token, pickSteps, projectUri.fsPath); - step = GENERATE_JAR; + step = ExportSteps.GenerateJar; break; } - case GENERATE_JAR: { + case ExportSteps.GenerateJar: { outputFileName = await generateJar(progress, token, pickSteps, rootNodes, pickResult, projectUri.fsPath); resolve(outputFileName); - step = FINISH; + step = ExportSteps.Finish; break; } } @@ -93,17 +95,22 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], return resolve(folders[0].uri.toString()); } progress.report({ increment: 10, message: "Selecting project..." }); - const pickNodes: JarQuickPickItem[] = []; + const pickNodes: IJarQuickPickItem[] = []; for (const folder of folders) { - pickNodes.push(new JarQuickPickItem(folder.name, folder.uri.fsPath, folder.uri.toString())); + const JarQuickPickItem: IJarQuickPickItem = { + label: folder.name, + description: folder.uri.fsPath, + uri: folder.uri.toString() + } + pickNodes.push(JarQuickPickItem); } - const pickBox = window.createQuickPick(); + const pickBox = window.createQuickPick(); pickBox.items = pickNodes; pickBox.title = "Export Jar - Determine project"; pickBox.placeholder = "Select the project..."; pickBox.ignoreFocusOut = true; pickBox.onDidAccept(() => { - pickSteps.push(SOLVE_PROJECT); + pickSteps.push(ExportSteps.ResolveProject); resolve(pickBox.selectedItems[0].uri); pickBox.dispose(); }); @@ -154,17 +161,25 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string return resolve(""); } progress.report({ increment: 30, message: "Determining main class..." }); - const pickNodes: JarQuickPickItem[] = []; + const pickNodes: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { - pickNodes.push(new JarQuickPickItem(getName(mainMethod), mainMethod.name)); + const JarQuickPickItem: IJarQuickPickItem = { + label: getName(mainMethod), + description: mainMethod.name + } + pickNodes.push(JarQuickPickItem); } } if (pickNodes.length === 0) { return resolve(""); } else { - const pickBox = window.createQuickPick(); - pickNodes.push(new JarQuickPickItem("No main class", "")); + const pickBox = window.createQuickPick(); + const noMainClassItem: IJarQuickPickItem = { + label: "No main class", + description: "" + } + pickNodes.push(noMainClassItem); pickBox.items = pickNodes; pickBox.title = "Export Jar - Determine main class"; pickBox.placeholder = "Select the main class..."; @@ -177,7 +192,7 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string } }); pickBox.onDidAccept(() => { - pickSteps.push(SOLVE_MAINMETHOD); + pickSteps.push(ExportSteps.ResolveMainMethod); resolve(pickBox.selectedItems[0].description); pickBox.dispose(); }); @@ -217,8 +232,8 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - const pickDependencies: JarQuickPickItem[] = []; - const pickedDependencies: JarQuickPickItem[] = []; + const pickDependencies: IJarQuickPickItem[] = []; + const pickedDependencies: IJarQuickPickItem[] = []; for (const rootNode of rootNodes) { const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); generateDependencies(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); @@ -233,7 +248,7 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], outClassPaths.push(pickDependencies[0].uri); return resolve(outClassPaths); } - const pickBox = window.createQuickPick(); + const pickBox = window.createQuickPick(); pickDependencies.sort((node1, node2) => { if (node1.description !== node2.description) { return node1.description.localeCompare(node2.description); @@ -276,7 +291,7 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], }); } -function generateDependencies(paths: string[], setUris: Set, pickDependencies: JarQuickPickItem[], +function generateDependencies(paths: string[], setUris: Set, pickDependencies: IJarQuickPickItem[], projectPath: string, isRuntime: boolean) { paths.forEach((classpath: string) => { const extName = extname(classpath); @@ -285,7 +300,14 @@ function generateDependencies(paths: string[], setUris: Set, pickDepende const type = (extName === ".jar") ? "external" : "internal"; if (!setUris.has(classpath)) { setUris.add(classpath); - pickDependencies.push(new JarQuickPickItem(baseName, description, classpath, type, isRuntime)); + const JarQuickPickItem: IJarQuickPickItem = { + label: baseName, + description: description, + uri: classpath, + type: type, + picked: isRuntime + } + pickDependencies.push(JarQuickPickItem); } }); } diff --git a/src/views/jarQuickPickItem.ts b/src/views/jarQuickPickItem.ts deleted file mode 100644 index b71ac184..00000000 --- a/src/views/jarQuickPickItem.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { QuickPickItem } from "vscode"; - -export class JarQuickPickItem implements QuickPickItem { - - public label: string; - public description: string; - public uri?: string; - public type?: string; - public picked?: boolean; - - constructor(_label: string, _description: string, _uri?: string, _type?: string, _picked?: boolean) { - this.label = _label; - this.description = _description; - this.uri = _uri; - this.type = _type; - this.picked = _picked; - } - -} From a27b68365103bba73323af0704c9aadd5f0dfcb1 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 21 Jul 2020 23:53:38 +0800 Subject: [PATCH 17/44] Apply changes --- src/views/build.ts | 2 +- src/views/exportJarFileUtil.ts | 40 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/views/build.ts b/src/views/build.ts index 12ca4dbc..cbec2952 100644 --- a/src/views/build.ts +++ b/src/views/build.ts @@ -4,8 +4,8 @@ import { basename } from "path"; import { commands, DiagnosticSeverity, languages, Uri, window } from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; -import { CompileWorkspaceStatus, resolveBuildFiles} from "../java/jdtls"; import { executeJavaExtensionCommand, JAVA_BUILD_WORKSPACE } from "../commands"; +import { CompileWorkspaceStatus, resolveBuildFiles } from "../java/jdtls"; import { UserError } from "../utility"; export async function buildWorkspace(): Promise { diff --git a/src/views/exportJarFileUtil.ts b/src/views/exportJarFileUtil.ts index b89e9804..acf7a06b 100644 --- a/src/views/exportJarFileUtil.ts +++ b/src/views/exportJarFileUtil.ts @@ -12,11 +12,11 @@ import { buildWorkspace } from "./build"; import { IJarQuickPickItem } from "./IJarQuickPickItem"; import { WorkspaceNode } from "./workspaceNode"; -enum ExportSteps{ +enum ExportSteps { ResolveProject = "RESOLVEPROJECT", ResolveMainMethod = "RESOLVEMAINMETHOD", GenerateJar = "GENERATEJAR", - Finish = "FINISH" + Finish = "FINISH", } let mainMethods: MainMethodInfo[]; @@ -97,12 +97,12 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], progress.report({ increment: 10, message: "Selecting project..." }); const pickNodes: IJarQuickPickItem[] = []; for (const folder of folders) { - const JarQuickPickItem: IJarQuickPickItem = { + const jarQuickPickItem: IJarQuickPickItem = { label: folder.name, description: folder.uri.fsPath, - uri: folder.uri.toString() - } - pickNodes.push(JarQuickPickItem); + uri: folder.uri.toString(), + }; + pickNodes.push(jarQuickPickItem); } const pickBox = window.createQuickPick(); pickBox.items = pickNodes; @@ -164,11 +164,11 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string const pickNodes: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { - const JarQuickPickItem: IJarQuickPickItem = { + const jarQuickPickItem: IJarQuickPickItem = { label: getName(mainMethod), - description: mainMethod.name - } - pickNodes.push(JarQuickPickItem); + description: mainMethod.name, + }; + pickNodes.push(jarQuickPickItem); } } if (pickNodes.length === 0) { @@ -177,8 +177,8 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string const pickBox = window.createQuickPick(); const noMainClassItem: IJarQuickPickItem = { label: "No main class", - description: "" - } + description: "", + }; pickNodes.push(noMainClassItem); pickBox.items = pickNodes; pickBox.title = "Export Jar - Determine main class"; @@ -296,18 +296,18 @@ function generateDependencies(paths: string[], setUris: Set, pickDepende paths.forEach((classpath: string) => { const extName = extname(classpath); const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); - const description = (isRuntime) ? "Runtime" : "Test"; - const type = (extName === ".jar") ? "external" : "internal"; + const descriptionValue = (isRuntime) ? "Runtime" : "Test"; + const typeValue = (extName === ".jar") ? "external" : "internal"; if (!setUris.has(classpath)) { setUris.add(classpath); - const JarQuickPickItem: IJarQuickPickItem = { + const jarQuickPickItem: IJarQuickPickItem = { label: baseName, - description: description, + description: descriptionValue, uri: classpath, - type: type, - picked: isRuntime - } - pickDependencies.push(JarQuickPickItem); + type: typeValue, + picked: isRuntime, + }; + pickDependencies.push(jarQuickPickItem); } }); } From de2c5f7661e6fe4953f4b64dc01c5b14e5571cb3 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 23 Jul 2020 00:48:32 +0800 Subject: [PATCH 18/44] Apply resolved changes --- src/commands.ts | 10 ++-- src/java/jdtls.ts | 20 +++---- src/views/IJarQuickPickItem.ts | 12 ----- src/views/build.ts | 10 ++-- src/views/exportJarFileUtil.ts | 99 +++++++++++++++++----------------- 5 files changed, 69 insertions(+), 82 deletions(-) delete mode 100644 src/views/IJarQuickPickItem.ts diff --git a/src/commands.ts b/src/commands.ts index b08c55eb..cef973e8 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -64,16 +64,14 @@ export namespace Commands { export const JAVA_PROJECT_EXPORTJAR = "java.project.exportJar"; export const VSCODE_OPEN_FOLDER = "vscode.openFolder"; -} - -export const JAVA_BUILD_WORKSPACE = "java.workspace.compile"; -export const JAVA_EXECUTE_WORKSPACE_COMMAND = "java.execute.workspaceCommand"; + export const JAVA_BUILD_WORKSPACE = "java.workspace.compile"; -export const JAVA_RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; + export const JAVA_RESOLVE_BUILD_FILES = "vscode.java.resolveBuildFiles"; +} export function executeJavaLanguageServerCommand(...rest) { - return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest); + return executeJavaExtensionCommand(Commands.EXECUTE_WORKSPACE_COMMAND, ...rest); } export async function executeJavaExtensionCommand(commandName: string, ...rest) { diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index 006f1b4b..6ac89c17 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import { commands } from "vscode"; -import { Commands, executeJavaLanguageServerCommand, JAVA_RESOLVE_BUILD_FILES } from "../commands"; +import { Commands, executeJavaLanguageServerCommand } from "../commands"; import { MainMethodInfo } from "../views/exportJarFileUtil"; import { INodeData } from "./nodeData"; @@ -30,15 +30,15 @@ export namespace Jdtls { export function exportJar(mainMethod: string, elements: string[], destination: string): Thenable { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_EXPORTJAR, mainMethod, elements, destination); } -} -export enum CompileWorkspaceStatus { - Failed = 0, - Succeed = 1, - Witherror = 2, - Cancelled = 3, -} + export enum CompileWorkspaceStatus { + Failed = 0, + Succeed = 1, + Witherror = 2, + Cancelled = 3, + } -export function resolveBuildFiles(): Promise { - return >executeJavaLanguageServerCommand(JAVA_RESOLVE_BUILD_FILES); + export function resolveBuildFiles(): Promise { + return >executeJavaLanguageServerCommand(Commands.JAVA_RESOLVE_BUILD_FILES); + } } diff --git a/src/views/IJarQuickPickItem.ts b/src/views/IJarQuickPickItem.ts deleted file mode 100644 index 3cac9386..00000000 --- a/src/views/IJarQuickPickItem.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { QuickPickItem } from "vscode"; - -export interface IJarQuickPickItem extends QuickPickItem { - label: string; - description: string; - uri?: string; - type?: string; - picked?: boolean; -} diff --git a/src/views/build.ts b/src/views/build.ts index cbec2952..9de5e447 100644 --- a/src/views/build.ts +++ b/src/views/build.ts @@ -4,15 +4,15 @@ import { basename } from "path"; import { commands, DiagnosticSeverity, languages, Uri, window } from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; -import { executeJavaExtensionCommand, JAVA_BUILD_WORKSPACE } from "../commands"; -import { CompileWorkspaceStatus, resolveBuildFiles } from "../java/jdtls"; +import { Commands, executeJavaExtensionCommand } from "../commands"; +import { Jdtls } from "../java/jdtls"; import { UserError } from "../utility"; export async function buildWorkspace(): Promise { const buildResult = await instrumentOperation("build", async (operationId: string) => { let error; try { - await executeJavaExtensionCommand(JAVA_BUILD_WORKSPACE, false); + await executeJavaExtensionCommand(Commands.JAVA_BUILD_WORKSPACE, false); } catch (err) { error = err; } @@ -36,7 +36,7 @@ async function handleBuildFailure(operationId: string, err: any): Promise(); - pickBox.items = pickNodes; - pickBox.title = "Export Jar - Determine project"; - pickBox.placeholder = "Select the project..."; - pickBox.ignoreFocusOut = true; + const pickBox = createPickBox("Export Jar - Determine project", "Select the project...", pickNodes, false, pickSteps.length); pickBox.onDidAccept(() => { pickSteps.push(ExportSteps.ResolveProject); resolve(pickBox.selectedItems[0].uri); @@ -126,7 +121,7 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], } function generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], - description: string, outputPath: string): Promise { + selectedMainMethod: string, outputPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { return reject("User Cancelled."); @@ -142,7 +137,7 @@ function generateJar(progress, token: CancellationToken, pickSteps: string[], ro } const outputFileName = join(outputPath, basename(outputPath) + ".jar"); progress.report({ increment: 30, message: "Generating jar..." }); - const exportResult = await Jdtls.exportJar(basename(description), outClassPaths, outputFileName); + const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFileName); if (exportResult === true) { resolve(outputFileName); } else { @@ -174,17 +169,12 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string if (pickNodes.length === 0) { return resolve(""); } else { - const pickBox = window.createQuickPick(); const noMainClassItem: IJarQuickPickItem = { label: "No main class", description: "", }; pickNodes.push(noMainClassItem); - pickBox.items = pickNodes; - pickBox.title = "Export Jar - Determine main class"; - pickBox.placeholder = "Select the main class..."; - pickBox.ignoreFocusOut = true; - pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; + const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, false, pickSteps.length); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); @@ -206,7 +196,7 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string } function failMessage(message: string) { - window.showInformationMessage(message, "Done"); + window.showErrorMessage(message, "Done"); } function successMessage(outputFileName: string) { @@ -232,15 +222,15 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - const pickDependencies: IJarQuickPickItem[] = []; + let pickDependencies: IJarQuickPickItem[] = []; const pickedDependencies: IJarQuickPickItem[] = []; for (const rootNode of rootNodes) { - const modulePaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - generateDependencies(modulePaths.classpaths, setUris, pickDependencies, projectPath, true); - generateDependencies(modulePaths.modulepaths, setUris, pickDependencies, projectPath, true); - const modulePathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - generateDependencies(modulePathsTest.classpaths, setUris, pickDependencies, projectPath, false); - generateDependencies(modulePathsTest.modulepaths, setUris, pickDependencies, projectPath, false); + const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + pickDependencies = pickDependencies.concat(generateDependencies(classPaths.classpaths, setUris, projectPath, true)); + pickDependencies = pickDependencies.concat(generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + pickDependencies = pickDependencies.concat(generateDependencies(classPathsTest.classpaths, setUris, projectPath, false)); + pickDependencies = pickDependencies.concat(generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } if (pickDependencies.length === 0) { return reject("No class path found."); @@ -248,7 +238,6 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], outClassPaths.push(pickDependencies[0].uri); return resolve(outClassPaths); } - const pickBox = window.createQuickPick(); pickDependencies.sort((node1, node2) => { if (node1.description !== node2.description) { return node1.description.localeCompare(node2.description); @@ -258,18 +247,13 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], } return node1.label.localeCompare(node2.label); }); - pickBox.items = pickDependencies; - pickDependencies.forEach((pickDependency) => { + for (const pickDependency of pickDependencies) { if (pickDependency.picked) { pickedDependencies.push(pickDependency); } - }); + } + const pickBox = createPickBox("Export Jar - Determine elements", "Select the elements...", pickDependencies, true, pickSteps.length); pickBox.selectedItems = pickedDependencies; - pickBox.title = "Export Jar - Determine elements"; - pickBox.placeholder = "Select the elements..."; - pickBox.canSelectMany = true; - pickBox.ignoreFocusOut = true; - pickBox.buttons = pickSteps.length > 0 ? [(QuickInputButtons.Back)] : []; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); @@ -277,9 +261,9 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], } }); pickBox.onDidAccept(() => { - pickBox.selectedItems.forEach((item) => { + for (const item of pickBox.selectedItems) { outClassPaths.push(item.uri); - }); + } resolve(outClassPaths); pickBox.dispose(); }); @@ -291,9 +275,21 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], }); } -function generateDependencies(paths: string[], setUris: Set, pickDependencies: IJarQuickPickItem[], - projectPath: string, isRuntime: boolean) { - paths.forEach((classpath: string) => { +function createPickBox(title: string, placeholder: string, items: IJarQuickPickItem[], + canSelectMany: boolean, pickStepLength: number): QuickPick { + const pickBox = window.createQuickPick(); + pickBox.title = title; + pickBox.placeholder = placeholder; + pickBox.canSelectMany = canSelectMany; + pickBox.items = items; + pickBox.ignoreFocusOut = true; + pickBox.buttons = pickStepLength > 0 ? [(QuickInputButtons.Back)] : []; + return pickBox; +} + +function generateDependencies(paths: string[], setUris: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { + const pickDependencies: IJarQuickPickItem[] = []; + for (const classpath of paths) { const extName = extname(classpath); const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); const descriptionValue = (isRuntime) ? "Runtime" : "Test"; @@ -309,15 +305,12 @@ function generateDependencies(paths: string[], setUris: Set, pickDepende }; pickDependencies.push(jarQuickPickItem); } - }); + } + return pickDependencies; } + function getName(data: MainMethodInfo) { - const point = data.name.lastIndexOf("."); - if (point === -1) { - return data.name; - } else { - return data.name.substring(point + 1); - } + return data.name.substring(data.name.lastIndexOf(".") + 1); } class ClasspathResult { @@ -334,3 +327,11 @@ export class MainMethodInfo { class InputFlowAction { public static back = new InputFlowAction(); } + +interface IJarQuickPickItem extends QuickPickItem { + label: string; + description: string; + uri?: string; + type?: string; + picked?: boolean; +} From ec9bd6717209c97f17e5124d856d76bff4d65a1b Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 23 Jul 2020 10:18:44 +0800 Subject: [PATCH 19/44] Apply changes --- src/{views => }/build.ts | 6 +++--- .../exportJarFileUtil.ts => exportJarFileCommand.ts} | 10 +++++----- src/java/jdtls.ts | 2 +- src/views/dependencyDataProvider.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/{views => }/build.ts (96%) rename src/{views/exportJarFileUtil.ts => exportJarFileCommand.ts} (98%) diff --git a/src/views/build.ts b/src/build.ts similarity index 96% rename from src/views/build.ts rename to src/build.ts index 9de5e447..11bcd6b9 100644 --- a/src/views/build.ts +++ b/src/build.ts @@ -4,9 +4,9 @@ import { basename } from "path"; import { commands, DiagnosticSeverity, languages, Uri, window } from "vscode"; import { instrumentOperation, sendInfo, sendOperationError, setErrorCode } from "vscode-extension-telemetry-wrapper"; -import { Commands, executeJavaExtensionCommand } from "../commands"; -import { Jdtls } from "../java/jdtls"; -import { UserError } from "../utility"; +import { Commands, executeJavaExtensionCommand } from "./commands"; +import { Jdtls } from "./java/jdtls"; +import { UserError } from "./utility"; export async function buildWorkspace(): Promise { const buildResult = await instrumentOperation("build", async (operationId: string) => { diff --git a/src/views/exportJarFileUtil.ts b/src/exportJarFileCommand.ts similarity index 98% rename from src/views/exportJarFileUtil.ts rename to src/exportJarFileCommand.ts index 568c367b..58ae583a 100644 --- a/src/views/exportJarFileUtil.ts +++ b/src/exportJarFileCommand.ts @@ -5,11 +5,11 @@ import { EOL, platform } from "os"; import { basename, extname, join } from "path"; import { CancellationToken, commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; -import { isStandardServerReady } from "../extension"; -import { Jdtls } from "../java/jdtls"; -import { INodeData } from "../java/nodeData"; import { buildWorkspace } from "./build"; -import { WorkspaceNode } from "./workspaceNode"; +import { isStandardServerReady } from "./extension"; +import { Jdtls } from "./java/jdtls"; +import { INodeData } from "./java/nodeData"; +import { WorkspaceNode } from "./views/workspaceNode"; enum ExportSteps { ResolveProject = "RESOLVEPROJECT", @@ -37,7 +37,6 @@ export async function createJarFile(node?: INodeData) { if (await buildWorkspace() === false) { return reject(); } - mainMethods = await Jdtls.getMainMethod(); const pickSteps: string[] = []; let step: string = ExportSteps.ResolveProject; let rootNodes: INodeData[] = []; @@ -152,6 +151,7 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string return reject("User Cancelled."); } progress.report({ increment: 10, message: "Resolving main classes..." }); + mainMethods = await Jdtls.getMainMethod(); if (mainMethods === undefined || mainMethods.length === 0) { return resolve(""); } diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index 6ac89c17..54834834 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,7 +3,7 @@ import { commands } from "vscode"; import { Commands, executeJavaLanguageServerCommand } from "../commands"; -import { MainMethodInfo } from "../views/exportJarFileUtil"; +import { MainMethodInfo } from "../exportJarFileCommand"; import { INodeData } from "./nodeData"; export namespace Jdtls { diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index 246004f6..001a1823 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -8,13 +8,13 @@ import { } from "vscode"; import { instrumentOperation, instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper"; import { Commands } from "../commands"; +import { createJarFile } from "../exportJarFileCommand"; import { isStandardServerReady, isSwitchingServer } from "../extension"; import { Jdtls } from "../java/jdtls"; import { INodeData, NodeKind } from "../java/nodeData"; import { Settings } from "../settings"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; -import { createJarFile } from "./exportJarFileUtil"; import { LightWeightNode } from "./lightWeightNode"; import { ProjectNode } from "./projectNode"; import { WorkspaceNode } from "./workspaceNode"; From 1666c3ab5d4692168798732bc4ae588f2f799de5 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 23 Jul 2020 11:21:31 +0800 Subject: [PATCH 20/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 58ae583a..481ad3d1 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -18,8 +18,6 @@ enum ExportSteps { Finish = "FINISH", } -let mainMethods: MainMethodInfo[]; - export async function createJarFile(node?: INodeData) { if (!isStandardServerReady()) { return; @@ -102,7 +100,7 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], }; pickNodes.push(jarQuickPickItem); } - const pickBox = createPickBox("Export Jar - Determine project", "Select the project...", pickNodes, false, pickSteps.length); + const pickBox = createPickBox("Export Jar - Determine project", "Select the project...", pickNodes, pickSteps.length > 0); pickBox.onDidAccept(() => { pickSteps.push(ExportSteps.ResolveProject); resolve(pickBox.selectedItems[0].uri); @@ -151,7 +149,7 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string return reject("User Cancelled."); } progress.report({ increment: 10, message: "Resolving main classes..." }); - mainMethods = await Jdtls.getMainMethod(); + const mainMethods: MainMethodInfo[] = await Jdtls.getMainMethod(); if (mainMethods === undefined || mainMethods.length === 0) { return resolve(""); } @@ -174,7 +172,7 @@ function resolveMainMethod(progress, token: CancellationToken, pickSteps: string description: "", }; pickNodes.push(noMainClassItem); - const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, false, pickSteps.length); + const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); @@ -222,15 +220,15 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - let pickDependencies: IJarQuickPickItem[] = []; + const pickDependencies: IJarQuickPickItem[] = []; const pickedDependencies: IJarQuickPickItem[] = []; for (const rootNode of rootNodes) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickDependencies = pickDependencies.concat(generateDependencies(classPaths.classpaths, setUris, projectPath, true)); - pickDependencies = pickDependencies.concat(generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true)); + pickDependencies.push(...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickDependencies = pickDependencies.concat(generateDependencies(classPathsTest.classpaths, setUris, projectPath, false)); - pickDependencies = pickDependencies.concat(generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); + pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false)); + pickDependencies.push(...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } if (pickDependencies.length === 0) { return reject("No class path found."); @@ -252,7 +250,7 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], pickedDependencies.push(pickDependency); } } - const pickBox = createPickBox("Export Jar - Determine elements", "Select the elements...", pickDependencies, true, pickSteps.length); + const pickBox = createPickBox("Export Jar - Determine elements", "Select the elements...", pickDependencies, pickSteps.length > 0, true); pickBox.selectedItems = pickedDependencies; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { @@ -276,14 +274,14 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], } function createPickBox(title: string, placeholder: string, items: IJarQuickPickItem[], - canSelectMany: boolean, pickStepLength: number): QuickPick { + backBtnEnabled: boolean, canSelectMany: boolean = false): QuickPick { const pickBox = window.createQuickPick(); pickBox.title = title; pickBox.placeholder = placeholder; pickBox.canSelectMany = canSelectMany; pickBox.items = items; pickBox.ignoreFocusOut = true; - pickBox.buttons = pickStepLength > 0 ? [(QuickInputButtons.Back)] : []; + pickBox.buttons = backBtnEnabled ? [(QuickInputButtons.Back)] : []; return pickBox; } @@ -329,9 +327,6 @@ class InputFlowAction { } interface IJarQuickPickItem extends QuickPickItem { - label: string; - description: string; uri?: string; type?: string; - picked?: boolean; } From 2fbcf790a96ab8810bf461e97ad70f85dee54b25 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 23 Jul 2020 13:08:05 +0800 Subject: [PATCH 21/44] Apply changes and modify view group --- package.json | 2 +- src/exportJarFileCommand.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0db36c40..dee130e5 100644 --- a/package.json +++ b/package.json @@ -216,7 +216,7 @@ { "command": "java.view.package.exportJar", "when": "view == javaProjectExplorer && java:serverMode!= LightWeight", - "group": "navigation@3" + "group": "navigation@50" }, { "command": "java.view.package.refresh", diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 481ad3d1..cfc2e830 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -224,11 +224,11 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const pickedDependencies: IJarQuickPickItem[] = []; for (const rootNode of rootNodes) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true)); - pickDependencies.push(...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), + ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false)); - pickDependencies.push(...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); + pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), + ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } if (pickDependencies.length === 0) { return reject("No class path found."); From e79a752ad33b0141ae949e7137f39fbffa14ecf0 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 23 Jul 2020 17:15:23 +0800 Subject: [PATCH 22/44] Template version remains more debugging --- .../jdtls/ext/core/CommandHandler.java | 2 +- .../jdtls/ext/core/PackageCommand.java | 2 +- .../jdtls/ext/core/ProjectCommand.java | 15 ++++++- src/exportJarFileCommand.ts | 39 +++++++++++-------- src/java/jdtls.ts | 4 +- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java index 5061ed15..c1356dcf 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/CommandHandler.java @@ -32,7 +32,7 @@ public Object executeCommand(String commandId, List arguments, IProgress case "java.resolvePath": return PackageCommand.resolvePath(arguments, monitor); case "java.project.getMainMethod": - return ProjectCommand.getMainMethod(monitor); + return ProjectCommand.getMainMethod(arguments, monitor); case "java.project.exportJar": return ProjectCommand.exportJar(arguments, monitor); default: 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 6b763833..27ba95fa 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 @@ -526,7 +526,7 @@ private static Object[] findJarDirectoryChildren(JarEntryDirectory directory, St return null; } - private static IJavaProject getJavaProject(String projectUri) { + public static IJavaProject getJavaProject(String projectUri) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IContainer[] containers = root.findContainersForLocationURI(JDTUtils.toURI(projectUri)); diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index e3b5ebaf..e17745fb 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -38,9 +38,11 @@ import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; @@ -176,9 +178,18 @@ private static void writeFileRecursively(File folder, JarOutputStream fJarOutput } } - public static List getMainMethod(IProgressMonitor monitor) throws Exception { + public static List getMainMethod(List arguments, IProgressMonitor monitor) throws Exception { + String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class); + IJavaProject javaProject = PackageCommand.getJavaProject(mainMethod); + IPackageFragmentRoot[] packages = javaProject.getAllPackageFragmentRoots(); + List searchRoots = new ArrayList<>(); + for (IPackageFragmentRoot packageFragmentRoot : packages) { + if (!packageFragmentRoot.isExternal()) { + searchRoots.add(packageFragmentRoot); + } + } final List res = new ArrayList<>(); - IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(searchRoots.toArray(IJavaElement[]::new)); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); SearchRequestor requestor = new SearchRequestor() { diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index cfc2e830..d62d0a2a 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -18,10 +18,13 @@ enum ExportSteps { Finish = "FINISH", } +let isExportingJar: boolean = false; + export async function createJarFile(node?: INodeData) { - if (!isStandardServerReady()) { + if (!isStandardServerReady() || isExportingJar) { return; } + isExportingJar = true; window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar... ", @@ -29,7 +32,7 @@ export async function createJarFile(node?: INodeData) { }, (progress, token): Promise => { return new Promise(async (resolve, reject) => { token.onCancellationRequested(() => { - return reject("User Cancelled."); + return reject(); }); progress.report({ increment: 10, message: "Building workspace..." }); if (await buildWorkspace() === false) { @@ -53,7 +56,7 @@ export async function createJarFile(node?: INodeData) { break; } case ExportSteps.ResolveMainMethod: { - selectedMainMethod = await resolveMainMethod(progress, token, pickSteps, projectUri.fsPath); + selectedMainMethod = await resolveMainMethod(progress, token, pickSteps, projectUri.toString()); step = ExportSteps.GenerateJar; break; } @@ -74,13 +77,19 @@ export async function createJarFile(node?: INodeData) { } } }); - }).then((message) => { successMessage(message); }, (err) => { failMessage(err); }); + }).then((message) => { + successMessage(message); + isExportingJar = false; + }, (err) => { + failMessage(err); + isExportingJar = false; + }); } function resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { return new Promise((resolve, reject) => { if (token.isCancellationRequested) { - return reject("User Cancelled."); + return reject(); } if (node instanceof WorkspaceNode) { return resolve(node.uri); @@ -121,7 +130,7 @@ function generateJar(progress, token: CancellationToken, pickSteps: string[], ro selectedMainMethod: string, outputPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { - return reject("User Cancelled."); + return reject(); } else if (rootNodes === undefined) { return reject("No project found."); } @@ -146,23 +155,21 @@ function generateJar(progress, token: CancellationToken, pickSteps: string[], ro function resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { return new Promise(async (resolve, reject) => { if (token.isCancellationRequested) { - return reject("User Cancelled."); + return reject(); } progress.report({ increment: 10, message: "Resolving main classes..." }); - const mainMethods: MainMethodInfo[] = await Jdtls.getMainMethod(); + const mainMethods: MainMethodInfo[] = await Jdtls.getMainMethod(projectPath); if (mainMethods === undefined || mainMethods.length === 0) { return resolve(""); } - progress.report({ increment: 30, message: "Determining main class..." }); + progress.report({ increment: 30, message: "" }); const pickNodes: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { - if (Uri.file(mainMethod.path).fsPath.includes(projectPath)) { - const jarQuickPickItem: IJarQuickPickItem = { - label: getName(mainMethod), - description: mainMethod.name, - }; - pickNodes.push(jarQuickPickItem); - } + const jarQuickPickItem: IJarQuickPickItem = { + label: getName(mainMethod), + description: mainMethod.name, + }; + pickNodes.push(jarQuickPickItem); } if (pickNodes.length === 0) { return resolve(""); diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index 54834834..ab547570 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -23,8 +23,8 @@ export namespace Jdtls { return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_RESOLVEPATH, params); } - export function getMainMethod(): Thenable { - return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINMETHOD); + export function getMainMethod(params: string): Thenable { + return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GETMAINMETHOD, params); } export function exportJar(mainMethod: string, elements: string[], destination: string): Thenable { From 57a8fd0700eefef1c9317d81720cdcc814a931e8 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Fri, 24 Jul 2020 00:28:44 +0800 Subject: [PATCH 23/44] Update ProjectCommand.java --- .../jdtls/ext/core/ProjectCommand.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java index e17745fb..3f0e82bc 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/ProjectCommand.java @@ -179,16 +179,20 @@ private static void writeFileRecursively(File folder, JarOutputStream fJarOutput } public static List getMainMethod(List arguments, IProgressMonitor monitor) throws Exception { - String mainMethod = gson.fromJson(gson.toJson(arguments.get(0)), String.class); - IJavaProject javaProject = PackageCommand.getJavaProject(mainMethod); - IPackageFragmentRoot[] packages = javaProject.getAllPackageFragmentRoots(); + List projectList = listProjects(arguments, monitor); + final List res = new ArrayList<>(); List searchRoots = new ArrayList<>(); - for (IPackageFragmentRoot packageFragmentRoot : packages) { - if (!packageFragmentRoot.isExternal()) { - searchRoots.add(packageFragmentRoot); + if (projectList.size() == 0) { + return res; + } + for (PackageNode project : projectList) { + IJavaProject javaProject = PackageCommand.getJavaProject(project.getUri()); + for (IPackageFragmentRoot packageFragmentRoot : javaProject.getAllPackageFragmentRoots()) { + if (!packageFragmentRoot.isExternal()) { + searchRoots.add(packageFragmentRoot); + } } } - final List res = new ArrayList<>(); IJavaSearchScope scope = SearchEngine.createJavaSearchScope(searchRoots.toArray(IJavaElement[]::new)); SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); From 69277f4347b0720618f45a3056cd0a938ca7f511 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Fri, 24 Jul 2020 11:24:54 +0800 Subject: [PATCH 24/44] Update progress message --- src/exportJarFileCommand.ts | 259 +++++++++++++++++++----------------- 1 file changed, 139 insertions(+), 120 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index d62d0a2a..3e81775f 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -3,8 +3,7 @@ import { EOL, platform } from "os"; import { basename, extname, join } from "path"; -import { CancellationToken, commands, Extension, extensions, ProgressLocation, - QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; +import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; import { buildWorkspace } from "./build"; import { isStandardServerReady } from "./extension"; import { Jdtls } from "./java/jdtls"; @@ -25,72 +24,59 @@ export async function createJarFile(node?: INodeData) { return; } isExportingJar = true; - window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar... ", - cancellable: true, - }, (progress, token): Promise => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - return reject(); - }); - progress.report({ increment: 10, message: "Building workspace..." }); - if (await buildWorkspace() === false) { - return reject(); - } - const pickSteps: string[] = []; - let step: string = ExportSteps.ResolveProject; - let rootNodes: INodeData[] = []; - let projectFolder: string; - let projectUri: Uri; - let selectedMainMethod: string; - let outputFilePath: string; - while (step !== ExportSteps.Finish) { - try { - switch (step) { - case ExportSteps.ResolveProject: { - projectFolder = await resolveProject(progress, token, pickSteps, node); - projectUri = Uri.parse(projectFolder); - rootNodes = await Jdtls.getProjects(projectUri.toString()); - step = ExportSteps.ResolveMainMethod; - break; - } - case ExportSteps.ResolveMainMethod: { - selectedMainMethod = await resolveMainMethod(progress, token, pickSteps, projectUri.toString()); - step = ExportSteps.GenerateJar; - break; - } - case ExportSteps.GenerateJar: { - outputFilePath = await generateJar(progress, token, pickSteps, rootNodes, selectedMainMethod, projectUri.fsPath); - resolve(outputFilePath); - step = ExportSteps.Finish; - break; - } + return new Promise(async (resolve, reject) => { + if (await buildWorkspace() === false) { + return reject(); + } + const pickSteps: string[] = []; + let step: string = ExportSteps.ResolveProject; + let rootNodes: INodeData[] = []; + let projectFolder: string; + let projectUri: Uri; + let selectedMainMethod: string; + let outputFilePath: string; + while (step !== ExportSteps.Finish) { + try { + switch (step) { + case ExportSteps.ResolveProject: { + projectFolder = await resolveProject(pickSteps, node); + projectUri = Uri.parse(projectFolder); + rootNodes = await Jdtls.getProjects(projectUri.toString()); + step = ExportSteps.ResolveMainMethod; + break; } - } catch (err) { - if (err === InputFlowAction.back) { - step = pickSteps.pop(); - continue; - } else { - return reject(err); + case ExportSteps.ResolveMainMethod: { + selectedMainMethod = await resolveMainMethod(pickSteps, projectUri.toString()); + step = ExportSteps.GenerateJar; + break; } + case ExportSteps.GenerateJar: { + outputFilePath = await generateJar(pickSteps, rootNodes, selectedMainMethod, projectUri.fsPath); + resolve(outputFilePath); + step = ExportSteps.Finish; + break; + } + } + } catch (err) { + if (err === InputFlowAction.back) { + step = pickSteps.pop(); + continue; + } else { + return reject(err); } } - }); + } }).then((message) => { successMessage(message); isExportingJar = false; }, (err) => { failMessage(err); isExportingJar = false; - }); + }); } -function resolveProject(progress, token: CancellationToken, pickSteps: string[], node?: INodeData): Promise { +function resolveProject(pickSteps: string[], node?: INodeData): Promise { return new Promise((resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } if (node instanceof WorkspaceNode) { return resolve(node.uri); } @@ -99,7 +85,6 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], if (folders.length === 1) { return resolve(folders[0].uri.toString()); } - progress.report({ increment: 10, message: "Selecting project..." }); const pickNodes: IJarQuickPickItem[] = []; for (const folder of folders) { const jarQuickPickItem: IJarQuickPickItem = { @@ -126,77 +111,99 @@ function resolveProject(progress, token: CancellationToken, pickSteps: string[], }); } -function generateJar(progress, token: CancellationToken, pickSteps: string[], rootNodes: INodeData[], +function generateJar(pickSteps: string[], rootNodes: INodeData[], selectedMainMethod: string, outputPath: string): Promise { return new Promise(async (resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } else if (rootNodes === undefined) { + if (rootNodes === undefined) { return reject("No project found."); } - progress.report({ increment: 10, message: "Resolving classpaths..." }); let outClassPaths: string[]; try { outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); } catch (e) { return reject(e); } - const outputFileName = join(outputPath, basename(outputPath) + ".jar"); - progress.report({ increment: 30, message: "Generating jar..." }); - const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFileName); - if (exportResult === true) { - resolve(outputFileName); - } else { - reject("Export jar failed."); - } + const outputFilePath = join(outputPath, basename(outputPath) + ".jar"); + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar... : Generating jar...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (innerResolve, innerReject) => { + token.onCancellationRequested(() => { + return reject(); + }); + const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFilePath); + if (exportResult === true) { + innerResolve(outputFilePath); + } else { + innerReject("Export jar failed."); + } + }); + }).then((path) => { + resolve(path); + }, (err) => { + reject(err); + }); }); } -function resolveMainMethod(progress, token: CancellationToken, pickSteps: string[], projectPath: string): Promise { +function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { return new Promise(async (resolve, reject) => { - if (token.isCancellationRequested) { - return reject(); - } - progress.report({ increment: 10, message: "Resolving main classes..." }); - const mainMethods: MainMethodInfo[] = await Jdtls.getMainMethod(projectPath); - if (mainMethods === undefined || mainMethods.length === 0) { - return resolve(""); - } - progress.report({ increment: 30, message: "" }); - const pickNodes: IJarQuickPickItem[] = []; - for (const mainMethod of mainMethods) { - const jarQuickPickItem: IJarQuickPickItem = { - label: getName(mainMethod), - description: mainMethod.name, - }; - pickNodes.push(jarQuickPickItem); - } - if (pickNodes.length === 0) { - return resolve(""); - } else { - const noMainClassItem: IJarQuickPickItem = { - label: "No main class", - description: "", - }; - pickNodes.push(noMainClassItem); - const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - pickBox.dispose(); - } + let mainMethods: MainMethodInfo[] = []; + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar... : Resolving main classes...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (innerResolve, innerReject) => { + token.onCancellationRequested(() => { + return innerReject(); + }); + mainMethods = await Jdtls.getMainMethod(projectPath); + innerResolve(); }); - pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveMainMethod); - resolve(pickBox.selectedItems[0].description); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - } + }).then(() => { + if (mainMethods === undefined || mainMethods.length === 0) { + return resolve(""); + } + const pickNodes: IJarQuickPickItem[] = []; + for (const mainMethod of mainMethods) { + const jarQuickPickItem: IJarQuickPickItem = { + label: getName(mainMethod), + description: mainMethod.name, + }; + pickNodes.push(jarQuickPickItem); + } + if (pickNodes.length === 0) { + return resolve(""); + } else { + const noMainClassItem: IJarQuickPickItem = { + label: "No main class", + description: "", + }; + pickNodes.push(noMainClassItem); + const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + pickSteps.push(ExportSteps.ResolveMainMethod); + resolve(pickBox.selectedItems[0].description); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + } + }, () => { + reject(); + }); }); } @@ -229,14 +236,26 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const setUris: Set = new Set(); const pickDependencies: IJarQuickPickItem[] = []; const pickedDependencies: IJarQuickPickItem[] = []; - for (const rootNode of rootNodes) { - const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), - ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); - const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), - ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); - } + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar... : Resolving classpaths...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (innerResolve, innerReject) => { + token.onCancellationRequested(() => { + return innerReject(); + }); + for await (const rootNode of rootNodes) { + const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), + ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), + ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); + } + innerResolve(); + }); + }); if (pickDependencies.length === 0) { return reject("No class path found."); } else if (pickDependencies.length === 1) { From e74c44142755451d71dd80e7fa5e20c823658f51 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 27 Jul 2020 15:17:30 +0800 Subject: [PATCH 25/44] Apply changes --- package.json | 4 +- src/exportJarFileCommand.ts | 319 +++++++++++++--------------- src/views/dependencyDataProvider.ts | 2 +- 3 files changed, 155 insertions(+), 170 deletions(-) diff --git a/package.json b/package.json index dee130e5..986c0219 100644 --- a/package.json +++ b/package.json @@ -215,7 +215,7 @@ "view/title": [ { "command": "java.view.package.exportJar", - "when": "view == javaProjectExplorer && java:serverMode!= LightWeight", + "when": "view == javaProjectExplorer && java:serverMode!= LightWeight && workspaceFolderCount != 0", "group": "navigation@50" }, { @@ -287,7 +287,7 @@ }, { "command": "java.view.package.exportJar", - "when": "view == javaProjectExplorer && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", + "when": "view == javaProjectExplorer && workspaceFolderCount != 0 && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", "group": "inline" } ] diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 3e81775f..afbab9b2 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -4,6 +4,7 @@ import { EOL, platform } from "os"; import { basename, extname, join } from "path"; import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; +import { sendOperationError } from "vscode-extension-telemetry-wrapper"; import { buildWorkspace } from "./build"; import { isStandardServerReady } from "./extension"; import { Jdtls } from "./java/jdtls"; @@ -24,17 +25,17 @@ export async function createJarFile(node?: INodeData) { return; } isExportingJar = true; + const pickSteps: string[] = []; + let step: string = ExportSteps.ResolveProject; + let rootNodes: INodeData[] = []; + let projectFolder: string; + let projectUri: Uri; + let selectedMainMethod: string; + let outputFilePath: string; return new Promise(async (resolve, reject) => { if (await buildWorkspace() === false) { return reject(); } - const pickSteps: string[] = []; - let step: string = ExportSteps.ResolveProject; - let rootNodes: INodeData[] = []; - let projectFolder: string; - let projectUri: Uri; - let selectedMainMethod: string; - let outputFilePath: string; while (step !== ExportSteps.Finish) { try { switch (step) { @@ -42,6 +43,9 @@ export async function createJarFile(node?: INodeData) { projectFolder = await resolveProject(pickSteps, node); projectUri = Uri.parse(projectFolder); rootNodes = await Jdtls.getProjects(projectUri.toString()); + if (rootNodes === undefined) { + throw new Error("No project found. Please make sure your project folder is open."); + } step = ExportSteps.ResolveMainMethod; break; } @@ -61,6 +65,8 @@ export async function createJarFile(node?: INodeData) { if (err === InputFlowAction.back) { step = pickSteps.pop(); continue; + } else if (err instanceof Error) { + return reject(err.message); } else { return reject(err); } @@ -75,139 +81,118 @@ export async function createJarFile(node?: INodeData) { }); } -function resolveProject(pickSteps: string[], node?: INodeData): Promise { +async function resolveProject(pickSteps: string[], node?: INodeData): Promise { + if (node instanceof WorkspaceNode) { + return node.uri; + } + const folders = workspace.workspaceFolders; + // Need not to check folders.length for the workspaceFolderCount has been checked in package.json + if (folders.length === 1) { + return folders[0].uri.toString(); + } + const pickNodes: IJarQuickPickItem[] = []; + for await (const folder of folders) { + const jarQuickPickItem: IJarQuickPickItem = { + label: folder.name, + description: folder.uri.fsPath, + uri: folder.uri.toString(), + }; + pickNodes.push(jarQuickPickItem); + } return new Promise((resolve, reject) => { - if (node instanceof WorkspaceNode) { - return resolve(node.uri); - } - const folders = workspace.workspaceFolders; - if (folders && folders.length) { - if (folders.length === 1) { - return resolve(folders[0].uri.toString()); - } - const pickNodes: IJarQuickPickItem[] = []; - for (const folder of folders) { - const jarQuickPickItem: IJarQuickPickItem = { - label: folder.name, - description: folder.uri.fsPath, - uri: folder.uri.toString(), - }; - pickNodes.push(jarQuickPickItem); - } - const pickBox = createPickBox("Export Jar - Determine project", "Select the project...", pickNodes, pickSteps.length > 0); - pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveProject); - resolve(pickBox.selectedItems[0].uri); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - } else { - return reject("No workspace folder found."); - } + const pickBox = createPickBox("Export Jar : Determine project", "Select the project...", pickNodes, pickSteps.length > 0); + pickBox.onDidAccept(() => { + pickSteps.push(ExportSteps.ResolveProject); + resolve(pickBox.selectedItems[0].uri); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); }); } -function generateJar(pickSteps: string[], rootNodes: INodeData[], - selectedMainMethod: string, outputPath: string): Promise { - return new Promise(async (resolve, reject) => { - if (rootNodes === undefined) { - return reject("No project found."); - } - let outClassPaths: string[]; - try { - outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); - } catch (e) { - return reject(e); - } - const outputFilePath = join(outputPath, basename(outputPath) + ".jar"); - await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar... : Generating jar...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (innerResolve, innerReject) => { - token.onCancellationRequested(() => { - return reject(); - }); - const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFilePath); - if (exportResult === true) { - innerResolve(outputFilePath); - } else { - innerReject("Export jar failed."); - } +async function generateJar(pickSteps: string[], rootNodes: INodeData[], + selectedMainMethod: string, outputPath: string): Promise { + let outClassPaths: string[]; + outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); + const outputFilePath = join(outputPath, basename(outputPath) + ".jar"); + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Generating jar...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); }); - }).then((path) => { - resolve(path); - }, (err) => { - reject(err); + const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFilePath); + if (exportResult === true) { + resolve(outputFilePath); + } else { + reject(new Error("Export jar failed.")); + } }); }); + return outputFilePath; } -function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { - let mainMethods: MainMethodInfo[] = []; - await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar... : Resolving main classes...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (innerResolve, innerReject) => { - token.onCancellationRequested(() => { - return innerReject(); - }); - mainMethods = await Jdtls.getMainMethod(projectPath); - innerResolve(); +async function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { + let mainMethods: MainMethodInfo[] = []; + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Resolving main classes...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); }); - }).then(() => { - if (mainMethods === undefined || mainMethods.length === 0) { - return resolve(""); - } - const pickNodes: IJarQuickPickItem[] = []; - for (const mainMethod of mainMethods) { - const jarQuickPickItem: IJarQuickPickItem = { - label: getName(mainMethod), - description: mainMethod.name, - }; - pickNodes.push(jarQuickPickItem); - } - if (pickNodes.length === 0) { - return resolve(""); - } else { - const noMainClassItem: IJarQuickPickItem = { - label: "No main class", - description: "", - }; - pickNodes.push(noMainClassItem); - const pickBox = createPickBox("Export Jar - Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveMainMethod); - resolve(pickBox.selectedItems[0].description); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); + mainMethods = await Jdtls.getMainMethod(projectPath); + resolve(); + }); + }); + if (mainMethods === undefined || mainMethods.length === 0) { + return ""; + } + const pickNodes: IJarQuickPickItem[] = []; + for await (const mainMethod of mainMethods) { + const jarQuickPickItem: IJarQuickPickItem = { + label: getName(mainMethod), + description: mainMethod.name, + }; + pickNodes.push(jarQuickPickItem); + } + const noMainClassItem: IJarQuickPickItem = { + label: "No main class", + description: "", + }; + pickNodes.push(noMainClassItem); + return new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + reject(InputFlowAction.back); + pickBox.dispose(); } - }, () => { + }); + pickBox.onDidAccept(() => { + pickSteps.push(ExportSteps.ResolveMainMethod); + resolve(pickBox.selectedItems[0].description); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { reject(); + pickBox.dispose(); }); + pickBox.show(); }); } function failMessage(message: string) { + sendOperationError("", "Export Jar", new Error(message)); window.showErrorMessage(message, "Done"); } @@ -229,54 +214,54 @@ function successMessage(outputFileName: string) { } async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { - return new Promise(async (resolve, reject) => { - const extension: Extension | undefined = extensions.getExtension("redhat.java"); - const extensionApi: any = await extension?.activate(); - const outClassPaths: string[] = []; - const setUris: Set = new Set(); - const pickDependencies: IJarQuickPickItem[] = []; - const pickedDependencies: IJarQuickPickItem[] = []; - await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar... : Resolving classpaths...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (innerResolve, innerReject) => { - token.onCancellationRequested(() => { - return innerReject(); - }); - for await (const rootNode of rootNodes) { - const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), - ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); - const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), - ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); - } - innerResolve(); + const extension: Extension | undefined = extensions.getExtension("redhat.java"); + const extensionApi: any = await extension?.activate(); + const outClassPaths: string[] = []; + const setUris: Set = new Set(); + const pickDependencies: IJarQuickPickItem[] = []; + const pickedDependencies: IJarQuickPickItem[] = []; + await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Resolving classpaths...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); }); - }); - if (pickDependencies.length === 0) { - return reject("No class path found."); - } else if (pickDependencies.length === 1) { - outClassPaths.push(pickDependencies[0].uri); - return resolve(outClassPaths); - } - pickDependencies.sort((node1, node2) => { - if (node1.description !== node2.description) { - return node1.description.localeCompare(node2.description); + for await (const rootNode of rootNodes) { + const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), + ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), + ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } - if (node1.type !== node2.type) { - return node2.type.localeCompare(node1.type); - } - return node1.label.localeCompare(node2.label); + resolve(); }); - for (const pickDependency of pickDependencies) { - if (pickDependency.picked) { - pickedDependencies.push(pickDependency); - } + }); + if (pickDependencies.length === 0) { + throw new Error("No class path found. Please make sure your project is valid."); + } else if (pickDependencies.length === 1) { + outClassPaths.push(pickDependencies[0].uri); + return outClassPaths; + } + pickDependencies.sort((node1, node2) => { + if (node1.description !== node2.description) { + return node1.description.localeCompare(node2.description); } - const pickBox = createPickBox("Export Jar - Determine elements", "Select the elements...", pickDependencies, pickSteps.length > 0, true); + if (node1.type !== node2.type) { + return node2.type.localeCompare(node1.type); + } + return node1.label.localeCompare(node2.label); + }); + for (const pickDependency of pickDependencies) { + if (pickDependency.picked) { + pickedDependencies.push(pickDependency); + } + } + return new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", pickDependencies, pickSteps.length > 0, true); pickBox.selectedItems = pickedDependencies; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index 001a1823..dd845491 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -31,7 +31,7 @@ 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_EXPORT_JAR, (node: INodeData) => createJarFile(node))); + context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, (node: INodeData) => createJarFile(node))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => From 9ddf4f84990a99f38cf7163815c29164dc3549dc Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 27 Jul 2020 17:15:29 +0800 Subject: [PATCH 26/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index afbab9b2..3f75e8ec 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -91,7 +91,7 @@ async function resolveProject(pickSteps: string[], node?: INodeData): Promise { let outClassPaths: string[]; outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); - const outputFilePath = join(outputPath, basename(outputPath) + ".jar"); - await window.withProgress({ + const outputFilePath = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Generating jar...", cancellable: true, @@ -128,9 +127,10 @@ async function generateJar(pickSteps: string[], rootNodes: INodeData[], token.onCancellationRequested(() => { return reject(); }); - const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, outputFilePath); + const destPath = join(outputPath, basename(outputPath) + ".jar"); + const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, destPath); if (exportResult === true) { - resolve(outputFilePath); + resolve(destPath); } else { reject(new Error("Export jar failed.")); } @@ -140,25 +140,23 @@ async function generateJar(pickSteps: string[], rootNodes: INodeData[], } async function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { - let mainMethods: MainMethodInfo[] = []; - await window.withProgress({ + const mainMethods: MainMethodInfo[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving main classes...", cancellable: true, }, (progress, token) => { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { token.onCancellationRequested(() => { return reject(); }); - mainMethods = await Jdtls.getMainMethod(projectPath); - resolve(); + resolve(await Jdtls.getMainMethod(projectPath)); }); }); if (mainMethods === undefined || mainMethods.length === 0) { return ""; } const pickNodes: IJarQuickPickItem[] = []; - for await (const mainMethod of mainMethods) { + for (const mainMethod of mainMethods) { const jarQuickPickItem: IJarQuickPickItem = { label: getName(mainMethod), description: mainMethod.name, @@ -218,26 +216,26 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - const pickDependencies: IJarQuickPickItem[] = []; const pickedDependencies: IJarQuickPickItem[] = []; - await window.withProgress({ + const pickDependencies: IJarQuickPickItem[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving classpaths...", cancellable: true, }, (progress, token) => { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { token.onCancellationRequested(() => { return reject(); }); - for await (const rootNode of rootNodes) { + const pickTemp: IJarQuickPickItem[] = []; + for (const rootNode of rootNodes) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickDependencies.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), + pickTemp.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickDependencies.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), + pickTemp.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } - resolve(); + resolve(pickTemp); }); }); if (pickDependencies.length === 0) { From e5d3fcd08c4b26cb09fdcdbeb53892c344940fbb Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 27 Jul 2020 18:39:10 +0800 Subject: [PATCH 27/44] Apply changes --- package.json | 2 +- src/exportJarFileCommand.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 986c0219..66d0ec87 100644 --- a/package.json +++ b/package.json @@ -287,7 +287,7 @@ }, { "command": "java.view.package.exportJar", - "when": "view == javaProjectExplorer && workspaceFolderCount != 0 && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", + "when": "view == javaProjectExplorer && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", "group": "inline" } ] diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 3f75e8ec..98d135b7 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -44,7 +44,7 @@ export async function createJarFile(node?: INodeData) { projectUri = Uri.parse(projectFolder); rootNodes = await Jdtls.getProjects(projectUri.toString()); if (rootNodes === undefined) { - throw new Error("No project found. Please make sure your project folder is open."); + throw new Error("No project found. Please make sure your project folder is opened."); } step = ExportSteps.ResolveMainMethod; break; @@ -86,7 +86,7 @@ async function resolveProject(pickSteps: string[], node?: INodeData): Promise Date: Mon, 27 Jul 2020 21:51:35 +0800 Subject: [PATCH 28/44] Apply changes --- src/exportJarFileCommand.ts | 69 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 98d135b7..84a7aa30 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -12,7 +12,7 @@ import { INodeData } from "./java/nodeData"; import { WorkspaceNode } from "./views/workspaceNode"; enum ExportSteps { - ResolveProject = "RESOLVEPROJECT", + ResolveWorkspace = "RESOLVEWORKSPACE", ResolveMainMethod = "RESOLVEMAINMETHOD", GenerateJar = "GENERATEJAR", Finish = "FINISH", @@ -25,24 +25,23 @@ export async function createJarFile(node?: INodeData) { return; } isExportingJar = true; - const pickSteps: string[] = []; - let step: string = ExportSteps.ResolveProject; - let rootNodes: INodeData[] = []; - let projectFolder: string; - let projectUri: Uri; - let selectedMainMethod: string; - let outputFilePath: string; return new Promise(async (resolve, reject) => { + const pickSteps: string[] = []; + let step: string = ExportSteps.ResolveWorkspace; + let selectedMainMethod: string; + let workspaceUri: Uri; + let rootNodes: INodeData[] = []; if (await buildWorkspace() === false) { return reject(); } while (step !== ExportSteps.Finish) { try { switch (step) { - case ExportSteps.ResolveProject: { - projectFolder = await resolveProject(pickSteps, node); - projectUri = Uri.parse(projectFolder); - rootNodes = await Jdtls.getProjects(projectUri.toString()); + case ExportSteps.ResolveWorkspace: { + let workspaceFolder: string; + workspaceFolder = await resolveWorkspaceFolder(pickSteps, node); + workspaceUri = Uri.parse(workspaceFolder); + rootNodes = await Jdtls.getProjects(workspaceUri.toString()); if (rootNodes === undefined) { throw new Error("No project found. Please make sure your project folder is opened."); } @@ -50,12 +49,13 @@ export async function createJarFile(node?: INodeData) { break; } case ExportSteps.ResolveMainMethod: { - selectedMainMethod = await resolveMainMethod(pickSteps, projectUri.toString()); + selectedMainMethod = await resolveMainMethod(pickSteps, workspaceUri.toString()); step = ExportSteps.GenerateJar; break; } case ExportSteps.GenerateJar: { - outputFilePath = await generateJar(pickSteps, rootNodes, selectedMainMethod, projectUri.fsPath); + let outputFilePath: string; + outputFilePath = await generateJar(pickSteps, rootNodes, selectedMainMethod, workspaceUri.fsPath); resolve(outputFilePath); step = ExportSteps.Finish; break; @@ -81,7 +81,7 @@ export async function createJarFile(node?: INodeData) { }); } -async function resolveProject(pickSteps: string[], node?: INodeData): Promise { +async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Promise { if (node instanceof WorkspaceNode) { return node.uri; } @@ -90,19 +90,19 @@ async function resolveProject(pickSteps: string[], node?: INodeData): Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine project", "Select the project...", pickNodes, pickSteps.length > 0); + const pickBox = createPickBox("Export Jar : Determine workspace", "Select the workspace...", pickItems, pickSteps.length > 0); pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveProject); + pickSteps.push(ExportSteps.ResolveWorkspace); resolve(pickBox.selectedItems[0].uri); pickBox.dispose(); }); @@ -118,7 +118,7 @@ async function generateJar(pickSteps: string[], rootNodes: INodeData[], selectedMainMethod: string, outputPath: string): Promise { let outClassPaths: string[]; outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); - const outputFilePath = await window.withProgress({ + return window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Generating jar...", cancellable: true, @@ -136,7 +136,6 @@ async function generateJar(pickSteps: string[], rootNodes: INodeData[], } }); }); - return outputFilePath; } async function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { @@ -216,8 +215,8 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], const extensionApi: any = await extension?.activate(); const outClassPaths: string[] = []; const setUris: Set = new Set(); - const pickedDependencies: IJarQuickPickItem[] = []; - const pickDependencies: IJarQuickPickItem[] = await window.withProgress({ + const pickedDependencyItems: IJarQuickPickItem[] = []; + const pickDependencyItems: IJarQuickPickItem[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving classpaths...", cancellable: true, @@ -226,25 +225,25 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], token.onCancellationRequested(() => { return reject(); }); - const pickTemp: IJarQuickPickItem[] = []; + const pickItems: IJarQuickPickItem[] = []; for (const rootNode of rootNodes) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickTemp.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), + pickItems.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickTemp.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), + pickItems.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); } - resolve(pickTemp); + resolve(pickItems); }); }); - if (pickDependencies.length === 0) { + if (pickDependencyItems.length === 0) { throw new Error("No class path found. Please make sure your project is valid."); - } else if (pickDependencies.length === 1) { - outClassPaths.push(pickDependencies[0].uri); + } else if (pickDependencyItems.length === 1) { + outClassPaths.push(pickDependencyItems[0].uri); return outClassPaths; } - pickDependencies.sort((node1, node2) => { + pickDependencyItems.sort((node1, node2) => { if (node1.description !== node2.description) { return node1.description.localeCompare(node2.description); } @@ -253,14 +252,14 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], } return node1.label.localeCompare(node2.label); }); - for (const pickDependency of pickDependencies) { + for (const pickDependency of pickDependencyItems) { if (pickDependency.picked) { - pickedDependencies.push(pickDependency); + pickedDependencyItems.push(pickDependency); } } return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", pickDependencies, pickSteps.length > 0, true); - pickBox.selectedItems = pickedDependencies; + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", pickDependencyItems, pickSteps.length > 0, true); + pickBox.selectedItems = pickedDependencyItems; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); From 12af080c9dd1cc5949932119cdbd1ab338aac836 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 27 Jul 2020 22:49:03 +0800 Subject: [PATCH 29/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 84a7aa30..bd67a869 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -100,7 +100,7 @@ async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Pr pickItems.push(jarQuickPickItem); } return new Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine workspace", "Select the workspace...", pickItems, pickSteps.length > 0); + const pickBox = createPickBox("Export Jar : Determine project", "Select the project...", pickItems, pickSteps.length > 0); pickBox.onDidAccept(() => { pickSteps.push(ExportSteps.ResolveWorkspace); resolve(pickBox.selectedItems[0].uri); From b696b10e1e09e298bdda7a85f76dcd7886781da1 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Mon, 27 Jul 2020 23:45:03 +0800 Subject: [PATCH 30/44] Fix classpath bug --- src/exportJarFileCommand.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index bd67a869..e52acabb 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +import { existsSync } from "fs"; import { EOL, platform } from "os"; import { basename, extname, join } from "path"; import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; @@ -296,6 +297,9 @@ function createPickBox(title: string, placeholder: string, items: IJarQuickPickI function generateDependencies(paths: string[], setUris: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { const pickDependencies: IJarQuickPickItem[] = []; for (const classpath of paths) { + if (!existsSync(classpath)) { + continue; + } const extName = extname(classpath); const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); const descriptionValue = (isRuntime) ? "Runtime" : "Test"; From 636f91792632de31a7cdc22f245c46292ee1f58b Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 28 Jul 2020 13:23:36 +0800 Subject: [PATCH 31/44] Rename Variables --- package-lock.json | 18 ++++----- src/exportJarFileCommand.ts | 74 ++++++++++++++++++------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98af8c03..7dbcc3ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -320,7 +320,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -860,7 +860,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1710,7 +1710,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -2686,7 +2686,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -4177,7 +4177,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -6531,7 +6531,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -6974,7 +6974,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -7321,7 +7321,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -9330,7 +9330,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index e52acabb..81ea8586 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -31,7 +31,7 @@ export async function createJarFile(node?: INodeData) { let step: string = ExportSteps.ResolveWorkspace; let selectedMainMethod: string; let workspaceUri: Uri; - let rootNodes: INodeData[] = []; + let projectList: INodeData[] = []; if (await buildWorkspace() === false) { return reject(); } @@ -42,8 +42,8 @@ export async function createJarFile(node?: INodeData) { let workspaceFolder: string; workspaceFolder = await resolveWorkspaceFolder(pickSteps, node); workspaceUri = Uri.parse(workspaceFolder); - rootNodes = await Jdtls.getProjects(workspaceUri.toString()); - if (rootNodes === undefined) { + projectList = await Jdtls.getProjects(workspaceUri.toString()); + if (projectList === undefined) { throw new Error("No project found. Please make sure your project folder is opened."); } step = ExportSteps.ResolveMainMethod; @@ -56,7 +56,7 @@ export async function createJarFile(node?: INodeData) { } case ExportSteps.GenerateJar: { let outputFilePath: string; - outputFilePath = await generateJar(pickSteps, rootNodes, selectedMainMethod, workspaceUri.fsPath); + outputFilePath = await generateJar(pickSteps, projectList, selectedMainMethod, workspaceUri.fsPath); resolve(outputFilePath); step = ExportSteps.Finish; break; @@ -115,10 +115,10 @@ async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Pr }); } -async function generateJar(pickSteps: string[], rootNodes: INodeData[], +async function generateJar(pickSteps: string[], projectList: INodeData[], selectedMainMethod: string, outputPath: string): Promise { - let outClassPaths: string[]; - outClassPaths = await generateOutClassPath(pickSteps, rootNodes, outputPath); + let elements: string[]; + elements = await generateElements(pickSteps, projectList, outputPath); return window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Generating jar...", @@ -129,7 +129,7 @@ async function generateJar(pickSteps: string[], rootNodes: INodeData[], return reject(); }); const destPath = join(outputPath, basename(outputPath) + ".jar"); - const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), outClassPaths, destPath); + const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), elements, destPath); if (exportResult === true) { resolve(destPath); } else { @@ -155,21 +155,21 @@ async function resolveMainMethod(pickSteps: string[], projectPath: string): Prom if (mainMethods === undefined || mainMethods.length === 0) { return ""; } - const pickNodes: IJarQuickPickItem[] = []; + const pickItems: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { const jarQuickPickItem: IJarQuickPickItem = { label: getName(mainMethod), description: mainMethod.name, }; - pickNodes.push(jarQuickPickItem); + pickItems.push(jarQuickPickItem); } const noMainClassItem: IJarQuickPickItem = { label: "No main class", description: "", }; - pickNodes.push(noMainClassItem); + pickItems.push(noMainClassItem); return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class...", pickNodes, pickSteps.length > 0); + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class...", pickItems, pickSteps.length > 0); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(InputFlowAction.back); @@ -211,13 +211,13 @@ function successMessage(outputFileName: string) { }); } -async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], projectPath: string): Promise { +async function generateElements(pickSteps: string[], projectList: INodeData[], projectPath: string): Promise { const extension: Extension | undefined = extensions.getExtension("redhat.java"); const extensionApi: any = await extension?.activate(); - const outClassPaths: string[] = []; - const setUris: Set = new Set(); + const elements: string[] = []; + const uriSet: Set = new Set(); const pickedDependencyItems: IJarQuickPickItem[] = []; - const pickDependencyItems: IJarQuickPickItem[] = await window.withProgress({ + const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving classpaths...", cancellable: true, @@ -227,24 +227,24 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], return reject(); }); const pickItems: IJarQuickPickItem[] = []; - for (const rootNode of rootNodes) { + for (const rootNode of projectList) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickItems.push(...generateDependencies(classPaths.classpaths, setUris, projectPath, true), - ...generateDependencies(classPaths.modulepaths, setUris, projectPath, true)); + pickItems.push(...generateDependencies(classPaths.classpaths, uriSet, projectPath, true), + ...generateDependencies(classPaths.modulepaths, uriSet, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickItems.push(...generateDependencies(classPathsTest.classpaths, setUris, projectPath, false), - ...generateDependencies(classPathsTest.modulepaths, setUris, projectPath, false)); + pickItems.push(...generateDependencies(classPathsTest.classpaths, uriSet, projectPath, false), + ...generateDependencies(classPathsTest.modulepaths, uriSet, projectPath, false)); } resolve(pickItems); }); }); - if (pickDependencyItems.length === 0) { - throw new Error("No class path found. Please make sure your project is valid."); - } else if (pickDependencyItems.length === 1) { - outClassPaths.push(pickDependencyItems[0].uri); - return outClassPaths; + if (dependencyItems.length === 0) { + throw new Error("No classpath found. Please make sure your project is valid."); + } else if (dependencyItems.length === 1) { + elements.push(dependencyItems[0].uri); + return elements; } - pickDependencyItems.sort((node1, node2) => { + dependencyItems.sort((node1, node2) => { if (node1.description !== node2.description) { return node1.description.localeCompare(node2.description); } @@ -253,13 +253,13 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], } return node1.label.localeCompare(node2.label); }); - for (const pickDependency of pickDependencyItems) { + for (const pickDependency of dependencyItems) { if (pickDependency.picked) { pickedDependencyItems.push(pickDependency); } } return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", pickDependencyItems, pickSteps.length > 0, true); + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", dependencyItems, pickSteps.length > 0, true); pickBox.selectedItems = pickedDependencyItems; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { @@ -269,9 +269,9 @@ async function generateOutClassPath(pickSteps: string[], rootNodes: INodeData[], }); pickBox.onDidAccept(() => { for (const item of pickBox.selectedItems) { - outClassPaths.push(item.uri); + elements.push(item.uri); } - resolve(outClassPaths); + resolve(elements); pickBox.dispose(); }); pickBox.onDidHide(() => { @@ -294,8 +294,8 @@ function createPickBox(title: string, placeholder: string, items: IJarQuickPickI return pickBox; } -function generateDependencies(paths: string[], setUris: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { - const pickDependencies: IJarQuickPickItem[] = []; +function generateDependencies(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { + const dependencyItems: IJarQuickPickItem[] = []; for (const classpath of paths) { if (!existsSync(classpath)) { continue; @@ -304,8 +304,8 @@ function generateDependencies(paths: string[], setUris: Set, projectPath const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); const descriptionValue = (isRuntime) ? "Runtime" : "Test"; const typeValue = (extName === ".jar") ? "external" : "internal"; - if (!setUris.has(classpath)) { - setUris.add(classpath); + if (!uriSet.has(classpath)) { + uriSet.add(classpath); const jarQuickPickItem: IJarQuickPickItem = { label: baseName, description: descriptionValue, @@ -313,10 +313,10 @@ function generateDependencies(paths: string[], setUris: Set, projectPath type: typeValue, picked: isRuntime, }; - pickDependencies.push(jarQuickPickItem); + dependencyItems.push(jarQuickPickItem); } } - return pickDependencies; + return dependencyItems; } function getName(data: MainMethodInfo) { From b145363eed4356ed79d7e1d214a548e5206f292a Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 28 Jul 2020 14:40:55 +0800 Subject: [PATCH 32/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 81ea8586..b34c8f1c 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -27,20 +27,19 @@ export async function createJarFile(node?: INodeData) { } isExportingJar = true; return new Promise(async (resolve, reject) => { - const pickSteps: string[] = []; - let step: string = ExportSteps.ResolveWorkspace; - let selectedMainMethod: string; - let workspaceUri: Uri; - let projectList: INodeData[] = []; if (await buildWorkspace() === false) { return reject(); } + const pickSteps: string[] = []; + let step: string = ExportSteps.ResolveWorkspace; while (step !== ExportSteps.Finish) { + let selectedMainMethod: string; + let workspaceUri: Uri; + let projectList: INodeData[] = []; try { switch (step) { case ExportSteps.ResolveWorkspace: { - let workspaceFolder: string; - workspaceFolder = await resolveWorkspaceFolder(pickSteps, node); + const workspaceFolder: string = await resolveWorkspaceFolder(pickSteps, node); workspaceUri = Uri.parse(workspaceFolder); projectList = await Jdtls.getProjects(workspaceUri.toString()); if (projectList === undefined) { @@ -55,9 +54,7 @@ export async function createJarFile(node?: INodeData) { break; } case ExportSteps.GenerateJar: { - let outputFilePath: string; - outputFilePath = await generateJar(pickSteps, projectList, selectedMainMethod, workspaceUri.fsPath); - resolve(outputFilePath); + resolve(await generateJar(pickSteps, projectList, selectedMainMethod, workspaceUri.fsPath)); step = ExportSteps.Finish; break; } @@ -117,8 +114,7 @@ async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Pr async function generateJar(pickSteps: string[], projectList: INodeData[], selectedMainMethod: string, outputPath: string): Promise { - let elements: string[]; - elements = await generateElements(pickSteps, projectList, outputPath); + const elements: string[] = await generateElements(pickSteps, projectList, outputPath); return window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Generating jar...", @@ -214,9 +210,6 @@ function successMessage(outputFileName: string) { async function generateElements(pickSteps: string[], projectList: INodeData[], projectPath: string): Promise { const extension: Extension | undefined = extensions.getExtension("redhat.java"); const extensionApi: any = await extension?.activate(); - const elements: string[] = []; - const uriSet: Set = new Set(); - const pickedDependencyItems: IJarQuickPickItem[] = []; const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving classpaths...", @@ -227,6 +220,7 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p return reject(); }); const pickItems: IJarQuickPickItem[] = []; + const uriSet: Set = new Set(); for (const rootNode of projectList) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); pickItems.push(...generateDependencies(classPaths.classpaths, uriSet, projectPath, true), @@ -238,6 +232,7 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p resolve(pickItems); }); }); + const elements: string[] = []; if (dependencyItems.length === 0) { throw new Error("No classpath found. Please make sure your project is valid."); } else if (dependencyItems.length === 1) { @@ -253,6 +248,7 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p } return node1.label.localeCompare(node2.label); }); + const pickedDependencyItems: IJarQuickPickItem[] = []; for (const pickDependency of dependencyItems) { if (pickDependency.picked) { pickedDependencyItems.push(pickDependency); From add8555a1ecc18e484e033a718870954ed94115c Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 28 Jul 2020 15:32:59 +0800 Subject: [PATCH 33/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 40 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index b34c8f1c..bc0c3fe2 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -32,10 +32,10 @@ export async function createJarFile(node?: INodeData) { } const pickSteps: string[] = []; let step: string = ExportSteps.ResolveWorkspace; + let selectedMainMethod: string; + let workspaceUri: Uri; + let projectList: INodeData[] = []; while (step !== ExportSteps.Finish) { - let selectedMainMethod: string; - let workspaceUri: Uri; - let projectList: INodeData[] = []; try { switch (step) { case ExportSteps.ResolveWorkspace: { @@ -90,12 +90,11 @@ async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Pr } const pickItems: IJarQuickPickItem[] = []; for (const folder of folders) { - const jarQuickPickItem: IJarQuickPickItem = { + pickItems.push({ label: folder.name, description: folder.uri.fsPath, uri: folder.uri.toString(), - }; - pickItems.push(jarQuickPickItem); + }); } return new Promise((resolve, reject) => { const pickBox = createPickBox("Export Jar : Determine project", "Select the project...", pickItems, pickSteps.length > 0); @@ -153,15 +152,13 @@ async function resolveMainMethod(pickSteps: string[], projectPath: string): Prom } const pickItems: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { - const jarQuickPickItem: IJarQuickPickItem = { + pickItems.push({ label: getName(mainMethod), description: mainMethod.name, - }; - pickItems.push(jarQuickPickItem); + }); } const noMainClassItem: IJarQuickPickItem = { - label: "No main class", - description: "", + label: "", }; pickItems.push(noMainClassItem); return new Promise(async (resolve, reject) => { @@ -223,11 +220,11 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p const uriSet: Set = new Set(); for (const rootNode of projectList) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickItems.push(...generateDependencies(classPaths.classpaths, uriSet, projectPath, true), - ...generateDependencies(classPaths.modulepaths, uriSet, projectPath, true)); + pickItems.push(...parseDependencyItems(classPaths.classpaths, uriSet, projectPath, true), + ...parseDependencyItems(classPaths.modulepaths, uriSet, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickItems.push(...generateDependencies(classPathsTest.classpaths, uriSet, projectPath, false), - ...generateDependencies(classPathsTest.modulepaths, uriSet, projectPath, false)); + pickItems.push(...parseDependencyItems(classPathsTest.classpaths, uriSet, projectPath, false), + ...parseDependencyItems(classPathsTest.modulepaths, uriSet, projectPath, false)); } resolve(pickItems); }); @@ -249,9 +246,9 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p return node1.label.localeCompare(node2.label); }); const pickedDependencyItems: IJarQuickPickItem[] = []; - for (const pickDependency of dependencyItems) { - if (pickDependency.picked) { - pickedDependencyItems.push(pickDependency); + for (const item of dependencyItems) { + if (item.picked) { + pickedDependencyItems.push(item); } } return new Promise(async (resolve, reject) => { @@ -290,7 +287,7 @@ function createPickBox(title: string, placeholder: string, items: IJarQuickPickI return pickBox; } -function generateDependencies(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { +function parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { const dependencyItems: IJarQuickPickItem[] = []; for (const classpath of paths) { if (!existsSync(classpath)) { @@ -302,14 +299,13 @@ function generateDependencies(paths: string[], uriSet: Set, projectPath: const typeValue = (extName === ".jar") ? "external" : "internal"; if (!uriSet.has(classpath)) { uriSet.add(classpath); - const jarQuickPickItem: IJarQuickPickItem = { + dependencyItems.push({ label: baseName, description: descriptionValue, uri: classpath, type: typeValue, picked: isRuntime, - }; - dependencyItems.push(jarQuickPickItem); + }); } } return dependencyItems; From 6da51276a4514feb7d4a5d1b410b9d16d0abd7b7 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 28 Jul 2020 16:02:45 +0800 Subject: [PATCH 34/44] Update exportJarFileCommand.ts --- src/exportJarFileCommand.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index bc0c3fe2..da02cadb 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { existsSync } from "fs"; +import { pathExists } from "fs-extra"; import { EOL, platform } from "os"; import { basename, extname, join } from "path"; import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; @@ -220,11 +220,11 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p const uriSet: Set = new Set(); for (const rootNode of projectList) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickItems.push(...parseDependencyItems(classPaths.classpaths, uriSet, projectPath, true), - ...parseDependencyItems(classPaths.modulepaths, uriSet, projectPath, true)); + pickItems.push(...await parseDependencyItems(classPaths.classpaths, uriSet, projectPath, true), + ...await parseDependencyItems(classPaths.modulepaths, uriSet, projectPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickItems.push(...parseDependencyItems(classPathsTest.classpaths, uriSet, projectPath, false), - ...parseDependencyItems(classPathsTest.modulepaths, uriSet, projectPath, false)); + pickItems.push(...await parseDependencyItems(classPathsTest.classpaths, uriSet, projectPath, false), + ...await parseDependencyItems(classPathsTest.modulepaths, uriSet, projectPath, false)); } resolve(pickItems); }); @@ -287,10 +287,10 @@ function createPickBox(title: string, placeholder: string, items: IJarQuickPickI return pickBox; } -function parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): IJarQuickPickItem[] { +async function parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): Promise { const dependencyItems: IJarQuickPickItem[] = []; for (const classpath of paths) { - if (!existsSync(classpath)) { + if (await pathExists(classpath) === false) { continue; } const extName = extname(classpath); From 3513237d1b8fdc6b6ae33a114cc14f7beb92df3b Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Tue, 28 Jul 2020 16:56:42 +0800 Subject: [PATCH 35/44] Change back button type --- src/exportJarFileCommand.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index da02cadb..5b1c7822 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -60,7 +60,7 @@ export async function createJarFile(node?: INodeData) { } } } catch (err) { - if (err === InputFlowAction.back) { + if (err === QuickInputButtons.Back) { step = pickSteps.pop(); continue; } else if (err instanceof Error) { @@ -165,7 +165,7 @@ async function resolveMainMethod(pickSteps: string[], projectPath: string): Prom const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class...", pickItems, pickSteps.length > 0); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); + reject(item); pickBox.dispose(); } }); @@ -256,7 +256,7 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p pickBox.selectedItems = pickedDependencyItems; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { - reject(InputFlowAction.back); + reject(item); pickBox.dispose(); } }); @@ -326,10 +326,6 @@ export class MainMethodInfo { public path: string; } -class InputFlowAction { - public static back = new InputFlowAction(); -} - interface IJarQuickPickItem extends QuickPickItem { uri?: string; type?: string; From 2744de0cb9d1ee0f5aa387c302f1f532bda74c66 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 29 Jul 2020 10:15:50 +0800 Subject: [PATCH 36/44] Update Placeholder message --- src/exportJarFileCommand.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 5b1c7822..a4b329da 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -97,7 +97,7 @@ async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Pr }); } return new Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine project", "Select the project...", pickItems, pickSteps.length > 0); + const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, pickSteps.length > 0); pickBox.onDidAccept(() => { pickSteps.push(ExportSteps.ResolveWorkspace); resolve(pickBox.selectedItems[0].uri); @@ -162,7 +162,7 @@ async function resolveMainMethod(pickSteps: string[], projectPath: string): Prom }; pickItems.push(noMainClassItem); return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class...", pickItems, pickSteps.length > 0); + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", pickItems, pickSteps.length > 0); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { reject(item); @@ -252,7 +252,7 @@ async function generateElements(pickSteps: string[], projectList: INodeData[], p } } return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements...", dependencyItems, pickSteps.length > 0, true); + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, pickSteps.length > 0, true); pickBox.selectedItems = pickedDependencyItems; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { From 22e57a7981882e60351313ff4e0cb4ba1a8dc114 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 29 Jul 2020 10:24:26 +0800 Subject: [PATCH 37/44] Rename command java.view.package.exportJar -> java.view.package.generateJar --- package.json | 10 +++++----- package.nls.json | 2 +- package.nls.zh.json | 2 +- src/commands.ts | 2 +- src/views/dependencyDataProvider.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 66d0ec87..edd285ef 100644 --- a/package.json +++ b/package.json @@ -106,8 +106,8 @@ "category": "Java" }, { - "command": "java.view.package.exportJar", - "title": "%contributes.commands.java.view.package.exportJar%", + "command": "java.view.package.generateJar", + "title": "%contributes.commands.java.view.package.generateJar%", "category": "Java", "icon": "$(arrow-down)" }, @@ -184,7 +184,7 @@ "when": "never" }, { - "command": "java.view.package.exportJar", + "command": "java.view.package.generateJar", "when": "java:serverMode != LightWeight" }, { @@ -214,7 +214,7 @@ ], "view/title": [ { - "command": "java.view.package.exportJar", + "command": "java.view.package.generateJar", "when": "view == javaProjectExplorer && java:serverMode!= LightWeight && workspaceFolderCount != 0", "group": "navigation@50" }, @@ -286,7 +286,7 @@ "group": "inline@0" }, { - "command": "java.view.package.exportJar", + "command": "java.view.package.generateJar", "when": "view == javaProjectExplorer && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", "group": "inline" } diff --git a/package.nls.json b/package.nls.json index 7c68803a..8b1bf113 100644 --- a/package.nls.json +++ b/package.nls.json @@ -11,7 +11,7 @@ "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.exportJar": "Export Jar...", + "contributes.commands.java.view.package.generateJar": "Export Jar...", "contributes.commands.java.view.package.copyFilePath": "Copy Path", "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "configuration.java.dependency.title": "Java Dependency Configuration", diff --git a/package.nls.zh.json b/package.nls.zh.json index 37454098..d7504068 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -11,7 +11,7 @@ "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.exportJar": "导出到 Jar 文件...", + "contributes.commands.java.view.package.generateJar": "导出到 Jar 文件...", "contributes.commands.java.view.package.copyFilePath": "复制路径", "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", diff --git a/src/commands.ts b/src/commands.ts index cef973e8..f7de6e0c 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -30,7 +30,7 @@ export namespace Commands { export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; - export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar"; + export const VIEW_PACKAGE_GENERATE_JAR = "java.view.package.generateJar"; export const JAVA_PROJECT_CREATE = "java.project.create"; diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index dd845491..ac5dfe51 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -31,7 +31,7 @@ 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(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, (node: INodeData) => createJarFile(node))); + context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_GENERATE_JAR, (node: INodeData) => createJarFile(node))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => From f3fbd4dbaa79ff04558f3f95f047ad19c4f0b2f7 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:01:14 +0800 Subject: [PATCH 38/44] Divide into steps (debug version) --- .../com.microsoft.jdtls.ext.core/plugin.xml | 2 +- .../jdtls/ext/core/CommandHandler.java | 2 +- package.json | 10 +- package.nls.json | 2 +- package.nls.zh.json | 2 +- src/commands.ts | 4 +- src/exportJarFileCommand.ts | 283 ++---------------- src/exportJarSteps/GenerateJarStep.ts | 151 ++++++++++ src/exportJarSteps/IStep.ts | 16 + src/exportJarSteps/ResolveMainMethodStep.ts | 80 +++++ src/exportJarSteps/ResolveWorkspaceStep.ts | 63 ++++ src/exportJarSteps/utility.ts | 21 ++ src/java/jdtls.ts | 4 +- src/views/dependencyDataProvider.ts | 2 +- 14 files changed, 371 insertions(+), 271 deletions(-) create mode 100644 src/exportJarSteps/GenerateJarStep.ts create mode 100644 src/exportJarSteps/IStep.ts create mode 100644 src/exportJarSteps/ResolveMainMethodStep.ts create mode 100644 src/exportJarSteps/ResolveWorkspaceStep.ts create mode 100644 src/exportJarSteps/utility.ts diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml index c36f3919..85fc9375 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/plugin.xml @@ -8,7 +8,7 @@ - + arguments, IProgress return PackageCommand.resolvePath(arguments, monitor); case "java.project.getMainMethod": return ProjectCommand.getMainMethod(arguments, monitor); - case "java.project.exportJar": + case "java.project.generateJar": return ProjectCommand.exportJar(arguments, monitor); default: break; diff --git a/package.json b/package.json index edd285ef..66d0ec87 100644 --- a/package.json +++ b/package.json @@ -106,8 +106,8 @@ "category": "Java" }, { - "command": "java.view.package.generateJar", - "title": "%contributes.commands.java.view.package.generateJar%", + "command": "java.view.package.exportJar", + "title": "%contributes.commands.java.view.package.exportJar%", "category": "Java", "icon": "$(arrow-down)" }, @@ -184,7 +184,7 @@ "when": "never" }, { - "command": "java.view.package.generateJar", + "command": "java.view.package.exportJar", "when": "java:serverMode != LightWeight" }, { @@ -214,7 +214,7 @@ ], "view/title": [ { - "command": "java.view.package.generateJar", + "command": "java.view.package.exportJar", "when": "view == javaProjectExplorer && java:serverMode!= LightWeight && workspaceFolderCount != 0", "group": "navigation@50" }, @@ -286,7 +286,7 @@ "group": "inline@0" }, { - "command": "java.view.package.generateJar", + "command": "java.view.package.exportJar", "when": "view == javaProjectExplorer && viewItem =~ /java:workspace.*?\\+uri/ && java:serverMode!= LightWeight", "group": "inline" } diff --git a/package.nls.json b/package.nls.json index 8b1bf113..7c68803a 100644 --- a/package.nls.json +++ b/package.nls.json @@ -11,7 +11,7 @@ "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.generateJar": "Export Jar...", + "contributes.commands.java.view.package.exportJar": "Export Jar...", "contributes.commands.java.view.package.copyFilePath": "Copy Path", "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "configuration.java.dependency.title": "Java Dependency Configuration", diff --git a/package.nls.zh.json b/package.nls.zh.json index d7504068..37454098 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -11,7 +11,7 @@ "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.generateJar": "导出到 Jar 文件...", + "contributes.commands.java.view.package.exportJar": "导出到 Jar 文件...", "contributes.commands.java.view.package.copyFilePath": "复制路径", "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "configuration.java.dependency.title": "Java 依赖管理配置", diff --git a/src/commands.ts b/src/commands.ts index f7de6e0c..65eba097 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -30,7 +30,7 @@ export namespace Commands { export const VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH = "java.view.package.copyRelativeFilePath"; - export const VIEW_PACKAGE_GENERATE_JAR = "java.view.package.generateJar"; + export const VIEW_PACKAGE_EXPORT_JAR = "java.view.package.exportJar"; export const JAVA_PROJECT_CREATE = "java.project.create"; @@ -61,7 +61,7 @@ export namespace Commands { export const JAVA_PROJECT_GETMAINMETHOD = "java.project.getMainMethod"; - export const JAVA_PROJECT_EXPORTJAR = "java.project.exportJar"; + export const JAVA_PROJECT_GENERATEJAR = "java.project.generateJar"; export const VSCODE_OPEN_FOLDER = "vscode.openFolder"; diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index a4b329da..0f844df0 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -7,20 +7,25 @@ import { basename, extname, join } from "path"; import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; import { sendOperationError } from "vscode-extension-telemetry-wrapper"; import { buildWorkspace } from "./build"; +import { GenerateJarStep } from "./exportJarSteps/GenerateJarStep"; +import { ExportSteps, IStep } from "./exportJarSteps/IStep"; +import { ResolveMainMethodStep } from "./exportJarSteps/ResolveMainMethodStep"; +import { ResolveWorkspaceStep } from "./exportJarSteps/ResolveWorkspaceStep"; import { isStandardServerReady } from "./extension"; import { Jdtls } from "./java/jdtls"; import { INodeData } from "./java/nodeData"; -import { WorkspaceNode } from "./views/workspaceNode"; - -enum ExportSteps { - ResolveWorkspace = "RESOLVEWORKSPACE", - ResolveMainMethod = "RESOLVEMAINMETHOD", - GenerateJar = "GENERATEJAR", - Finish = "FINISH", -} let isExportingJar: boolean = false; +export class GenerateSettings { + public entry?: INodeData; + public workspaceUri?: Uri; + public projectList?: INodeData[]; + public selectedMainMethod?: string; + public outputPath?: string; + public elements: string[]; +} + export async function createJarFile(node?: INodeData) { if (!isStandardServerReady() || isExportingJar) { return; @@ -31,45 +36,39 @@ export async function createJarFile(node?: INodeData) { return reject(); } const pickSteps: string[] = []; - let step: string = ExportSteps.ResolveWorkspace; - let selectedMainMethod: string; - let workspaceUri: Uri; - let projectList: INodeData[] = []; + let step: ExportSteps = ExportSteps.ResolveWorkspace; + const resolveWorkspaceStep: ResolveWorkspaceStep = new ResolveWorkspaceStep(); + const resolveMainMethodStep: ResolveMainMethodStep = new ResolveMainMethodStep(); + const generateJarStep: GenerateJarStep = new GenerateJarStep(); + const generateSettings: GenerateSettings = { + entry: node, + elements: [], + }; while (step !== ExportSteps.Finish) { try { switch (step) { case ExportSteps.ResolveWorkspace: { - const workspaceFolder: string = await resolveWorkspaceFolder(pickSteps, node); - workspaceUri = Uri.parse(workspaceFolder); - projectList = await Jdtls.getProjects(workspaceUri.toString()); - if (projectList === undefined) { - throw new Error("No project found. Please make sure your project folder is opened."); - } - step = ExportSteps.ResolveMainMethod; + step = await resolveWorkspaceStep.execute(undefined, generateSettings); break; } case ExportSteps.ResolveMainMethod: { - selectedMainMethod = await resolveMainMethod(pickSteps, workspaceUri.toString()); - step = ExportSteps.GenerateJar; + step = await resolveMainMethodStep.execute(resolveWorkspaceStep, generateSettings); break; } case ExportSteps.GenerateJar: { - resolve(await generateJar(pickSteps, projectList, selectedMainMethod, workspaceUri.fsPath)); - step = ExportSteps.Finish; + step = await generateJarStep.execute(resolveMainMethodStep, generateSettings); break; } } } catch (err) { - if (err === QuickInputButtons.Back) { - step = pickSteps.pop(); - continue; - } else if (err instanceof Error) { + if (err instanceof Error) { return reject(err.message); } else { return reject(err); } } } + resolve(generateSettings.outputPath); }).then((message) => { successMessage(message); isExportingJar = false; @@ -79,109 +78,6 @@ export async function createJarFile(node?: INodeData) { }); } -async function resolveWorkspaceFolder(pickSteps: string[], node?: INodeData): Promise { - if (node instanceof WorkspaceNode) { - return node.uri; - } - const folders = workspace.workspaceFolders; - // Guarded by workspaceFolderCount != 0 in package.json - if (folders.length === 1) { - return folders[0].uri.toString(); - } - const pickItems: IJarQuickPickItem[] = []; - for (const folder of folders) { - pickItems.push({ - label: folder.name, - description: folder.uri.fsPath, - uri: folder.uri.toString(), - }); - } - return new Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, pickSteps.length > 0); - pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveWorkspace); - resolve(pickBox.selectedItems[0].uri); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - }); -} - -async function generateJar(pickSteps: string[], projectList: INodeData[], - selectedMainMethod: string, outputPath: string): Promise { - const elements: string[] = await generateElements(pickSteps, projectList, outputPath); - return window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar : Generating jar...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - return reject(); - }); - const destPath = join(outputPath, basename(outputPath) + ".jar"); - const exportResult = await Jdtls.exportJar(basename(selectedMainMethod), elements, destPath); - if (exportResult === true) { - resolve(destPath); - } else { - reject(new Error("Export jar failed.")); - } - }); - }); -} - -async function resolveMainMethod(pickSteps: string[], projectPath: string): Promise { - const mainMethods: MainMethodInfo[] = await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar : Resolving main classes...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - return reject(); - }); - resolve(await Jdtls.getMainMethod(projectPath)); - }); - }); - if (mainMethods === undefined || mainMethods.length === 0) { - return ""; - } - const pickItems: IJarQuickPickItem[] = []; - for (const mainMethod of mainMethods) { - pickItems.push({ - label: getName(mainMethod), - description: mainMethod.name, - }); - } - const noMainClassItem: IJarQuickPickItem = { - label: "", - }; - pickItems.push(noMainClassItem); - return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", pickItems, pickSteps.length > 0); - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(item); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - pickSteps.push(ExportSteps.ResolveMainMethod); - resolve(pickBox.selectedItems[0].description); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - }); -} - function failMessage(message: string) { sendOperationError("", "Export Jar", new Error(message)); window.showErrorMessage(message, "Done"); @@ -203,130 +99,3 @@ function successMessage(outputFileName: string) { } }); } - -async function generateElements(pickSteps: string[], projectList: INodeData[], projectPath: string): Promise { - const extension: Extension | undefined = extensions.getExtension("redhat.java"); - const extensionApi: any = await extension?.activate(); - const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar : Resolving classpaths...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - return reject(); - }); - const pickItems: IJarQuickPickItem[] = []; - const uriSet: Set = new Set(); - for (const rootNode of projectList) { - const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickItems.push(...await parseDependencyItems(classPaths.classpaths, uriSet, projectPath, true), - ...await parseDependencyItems(classPaths.modulepaths, uriSet, projectPath, true)); - const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickItems.push(...await parseDependencyItems(classPathsTest.classpaths, uriSet, projectPath, false), - ...await parseDependencyItems(classPathsTest.modulepaths, uriSet, projectPath, false)); - } - resolve(pickItems); - }); - }); - const elements: string[] = []; - if (dependencyItems.length === 0) { - throw new Error("No classpath found. Please make sure your project is valid."); - } else if (dependencyItems.length === 1) { - elements.push(dependencyItems[0].uri); - return elements; - } - dependencyItems.sort((node1, node2) => { - if (node1.description !== node2.description) { - return node1.description.localeCompare(node2.description); - } - if (node1.type !== node2.type) { - return node2.type.localeCompare(node1.type); - } - return node1.label.localeCompare(node2.label); - }); - const pickedDependencyItems: IJarQuickPickItem[] = []; - for (const item of dependencyItems) { - if (item.picked) { - pickedDependencyItems.push(item); - } - } - return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, pickSteps.length > 0, true); - pickBox.selectedItems = pickedDependencyItems; - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - reject(item); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - for (const item of pickBox.selectedItems) { - elements.push(item.uri); - } - resolve(elements); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - }); -} - -function createPickBox(title: string, placeholder: string, items: IJarQuickPickItem[], - backBtnEnabled: boolean, canSelectMany: boolean = false): QuickPick { - const pickBox = window.createQuickPick(); - pickBox.title = title; - pickBox.placeholder = placeholder; - pickBox.canSelectMany = canSelectMany; - pickBox.items = items; - pickBox.ignoreFocusOut = true; - pickBox.buttons = backBtnEnabled ? [(QuickInputButtons.Back)] : []; - return pickBox; -} - -async function parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): Promise { - const dependencyItems: IJarQuickPickItem[] = []; - for (const classpath of paths) { - if (await pathExists(classpath) === false) { - continue; - } - const extName = extname(classpath); - const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); - const descriptionValue = (isRuntime) ? "Runtime" : "Test"; - const typeValue = (extName === ".jar") ? "external" : "internal"; - if (!uriSet.has(classpath)) { - uriSet.add(classpath); - dependencyItems.push({ - label: baseName, - description: descriptionValue, - uri: classpath, - type: typeValue, - picked: isRuntime, - }); - } - } - return dependencyItems; -} - -function getName(data: MainMethodInfo) { - return data.name.substring(data.name.lastIndexOf(".") + 1); -} - -class ClasspathResult { - public projectRoot: string; - public classpaths: string[]; - public modulepaths: string[]; -} - -export class MainMethodInfo { - public name: string; - public path: string; -} - -interface IJarQuickPickItem extends QuickPickItem { - uri?: string; - type?: string; -} diff --git a/src/exportJarSteps/GenerateJarStep.ts b/src/exportJarSteps/GenerateJarStep.ts new file mode 100644 index 00000000..5c3acd90 --- /dev/null +++ b/src/exportJarSteps/GenerateJarStep.ts @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { pathExists } from "fs-extra"; +import { EOL, platform } from "os"; +import { basename, extname, join } from "path"; +import { Extension, extensions, ProgressLocation, QuickInputButtons, window } from "vscode"; +import { GenerateSettings } from "../exportJarFileCommand"; +import { Jdtls } from "../java/jdtls"; +import { INodeData } from "../java/nodeData"; +import { ExportSteps, IStep } from "./IStep"; +import { createPickBox, IJarQuickPickItem } from "./utility"; + +export class GenerateJarStep implements IStep { + + public exportStep: ExportSteps; + + constructor() { + this.exportStep = ExportSteps.GenerateJar; + } + + public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + return await this.generateJar(lastStep, generateSettings) ? ExportSteps.Finish : lastStep.exportStep; + } + + private async generateJar(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + if (await this.generateElements(lastStep, generateSettings) === false) { + return false; + } + return window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Generating jar...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); + }); + const destPath = join(generateSettings.workspaceUri.fsPath, basename(generateSettings.workspaceUri.fsPath) + ".jar"); + const exportResult = await Jdtls.exportJar(basename(generateSettings.selectedMainMethod), generateSettings.elements, destPath); + if (exportResult === true) { + generateSettings.outputPath = destPath; + resolve(true); + } else { + reject(new Error("Export jar failed.")); + } + }); + }); + } + + private async generateElements(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + const extension: Extension | undefined = extensions.getExtension("redhat.java"); + const extensionApi: any = await extension?.activate(); + const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Resolving classpaths...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); + }); + const pickItems: IJarQuickPickItem[] = []; + const uriSet: Set = new Set(); + for (const rootNode of generateSettings.projectList) { + const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); + pickItems.push(...await this.parseDependencyItems(classPaths.classpaths, uriSet, generateSettings.workspaceUri.fsPath, true), + ...await this.parseDependencyItems(classPaths.modulepaths, uriSet, generateSettings.workspaceUri.fsPath, true)); + const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); + pickItems.push(...await this.parseDependencyItems(classPathsTest.classpaths, uriSet, generateSettings.workspaceUri.fsPath, false), + ...await this.parseDependencyItems(classPathsTest.modulepaths, uriSet, generateSettings.workspaceUri.fsPath, false)); + } + resolve(pickItems); + }); + }); + if (dependencyItems.length === 0) { + throw new Error("No classpath found. Please make sure your project is valid."); + } else if (dependencyItems.length === 1) { + generateSettings.elements.push(dependencyItems[0].uri); + return true; + } + dependencyItems.sort((node1, node2) => { + if (node1.description !== node2.description) { + return node1.description.localeCompare(node2.description); + } + if (node1.type !== node2.type) { + return node2.type.localeCompare(node1.type); + } + return node1.label.localeCompare(node2.label); + }); + const pickedDependencyItems: IJarQuickPickItem[] = []; + for (const item of dependencyItems) { + if (item.picked) { + pickedDependencyItems.push(item); + } + } + return new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, lastStep !== undefined, true); + pickBox.selectedItems = pickedDependencyItems; + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + resolve(false); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + for (const item of pickBox.selectedItems) { + generateSettings.elements.push(item.uri); + } + resolve(true); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + }); + } + + private async parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): Promise { + const dependencyItems: IJarQuickPickItem[] = []; + for (const classpath of paths) { + if (await pathExists(classpath) === false) { + continue; + } + const extName = extname(classpath); + const baseName = (extName === ".jar") ? basename(classpath) : classpath.substring(projectPath.length + 1); + const descriptionValue = (isRuntime) ? "Runtime" : "Test"; + const typeValue = (extName === ".jar") ? "external" : "internal"; + if (!uriSet.has(classpath)) { + uriSet.add(classpath); + dependencyItems.push({ + label: baseName, + description: descriptionValue, + uri: classpath, + type: typeValue, + picked: isRuntime, + }); + } + } + return dependencyItems; + } + +} + +class ClasspathResult { + public projectRoot: string; + public classpaths: string[]; + public modulepaths: string[]; +} diff --git a/src/exportJarSteps/IStep.ts b/src/exportJarSteps/IStep.ts new file mode 100644 index 00000000..2d318168 --- /dev/null +++ b/src/exportJarSteps/IStep.ts @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { GenerateSettings } from "../exportJarFileCommand"; + +export interface IStep { + exportStep: ExportSteps; + execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise; +} + +export enum ExportSteps { + ResolveWorkspace = "RESOLVEWORKSPACE", + ResolveMainMethod = "RESOLVEMAINMETHOD", + GenerateJar = "GENERATEJAR", + Finish = "FINISH", +} diff --git a/src/exportJarSteps/ResolveMainMethodStep.ts b/src/exportJarSteps/ResolveMainMethodStep.ts new file mode 100644 index 00000000..857c30ee --- /dev/null +++ b/src/exportJarSteps/ResolveMainMethodStep.ts @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ProgressLocation, QuickInputButtons, window } from "vscode"; +import { GenerateSettings } from "../exportJarFileCommand"; +import { Jdtls } from "../java/jdtls"; +import { ExportSteps, IStep } from "./IStep"; +import { createPickBox, IJarQuickPickItem } from "./utility"; + +export class ResolveMainMethodStep implements IStep { + + private static getName(data: MainMethodInfo) { + return data.name.substring(data.name.lastIndexOf(".") + 1); + } + + public exportStep; + + constructor() { + this.exportStep = ExportSteps.ResolveMainMethod; + } + + public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + return await this.resolveMainMethod(lastStep, generateSettings) ? ExportSteps.GenerateJar : lastStep.exportStep; + } + + private async resolveMainMethod(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + const mainMethods: MainMethodInfo[] = await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Resolving main classes...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); + }); + resolve(await Jdtls.getMainMethod(generateSettings.workspaceUri.toString())); + }); + }); + if (mainMethods === undefined || mainMethods.length === 0) { + generateSettings.selectedMainMethod = ""; + return true; + } + const pickItems: IJarQuickPickItem[] = []; + for (const mainMethod of mainMethods) { + pickItems.push({ + label: ResolveMainMethodStep.getName(mainMethod), + description: mainMethod.name, + }); + } + const noMainClassItem: IJarQuickPickItem = { + label: "", + }; + pickItems.push(noMainClassItem); + return new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", pickItems, lastStep !== undefined); + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + resolve(false); + pickBox.dispose(); + } + }); + pickBox.onDidAccept(() => { + generateSettings.selectedMainMethod = pickBox.selectedItems[0].description; + resolve(true); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + }); + } + +} + +export class MainMethodInfo { + public name: string; + public path: string; +} diff --git a/src/exportJarSteps/ResolveWorkspaceStep.ts b/src/exportJarSteps/ResolveWorkspaceStep.ts new file mode 100644 index 00000000..427a097c --- /dev/null +++ b/src/exportJarSteps/ResolveWorkspaceStep.ts @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Uri, workspace } from "vscode"; +import { GenerateSettings } from "../exportJarFileCommand"; +import { Jdtls } from "../java/jdtls"; +import { INodeData } from "../java/nodeData"; +import { WorkspaceNode } from "../views/workspaceNode"; +import { ExportSteps, IStep } from "./IStep"; +import { createPickBox, IJarQuickPickItem } from "./utility"; + +export class ResolveWorkspaceStep implements IStep { + + public exportStep; + + constructor() { + this.exportStep = ExportSteps.ResolveWorkspace; + } + + public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + await this.resolveWorkspaceFolder(lastStep, generateSettings, generateSettings.entry); + generateSettings.projectList = await Jdtls.getProjects(generateSettings.workspaceUri.toString()); + if (generateSettings.projectList === undefined) { + throw new Error("No project found. Please make sure your project folder is opened."); + } + return ExportSteps.ResolveMainMethod; + } + + private async resolveWorkspaceFolder(lastStep: IStep | undefined, generateSettings: GenerateSettings, + node?: INodeData): Promise { + if (node instanceof WorkspaceNode) { + generateSettings.workspaceUri = Uri.parse(node.uri); + return true; + } + const folders = workspace.workspaceFolders; + // Guarded by workspaceFolderCount != 0 in package.json + if (folders.length === 1) { + generateSettings.workspaceUri = Uri.parse(folders[0].uri.toString()); + return true; + } + const pickItems: IJarQuickPickItem[] = []; + for (const folder of folders) { + pickItems.push({ + label: folder.name, + description: folder.uri.fsPath, + uri: folder.uri.toString(), + }); + } + return new Promise((resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, lastStep !== undefined); + pickBox.onDidAccept(() => { + generateSettings.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); + resolve(true); + pickBox.dispose(); + }); + pickBox.onDidHide(() => { + reject(); + pickBox.dispose(); + }); + pickBox.show(); + }); + } +} diff --git a/src/exportJarSteps/utility.ts b/src/exportJarSteps/utility.ts new file mode 100644 index 00000000..14a31407 --- /dev/null +++ b/src/exportJarSteps/utility.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { QuickInputButtons, QuickPickItem, window, QuickPick } from "vscode"; + +export interface IJarQuickPickItem extends QuickPickItem { + uri?: string; + type?: string; +} + +export function createPickBox(title: string, placeholder: string, items: IJarQuickPickItem[], + backBtnEnabled: boolean, canSelectMany: boolean = false): QuickPick { + const pickBox = window.createQuickPick(); + pickBox.title = title; + pickBox.placeholder = placeholder; + pickBox.canSelectMany = canSelectMany; + pickBox.items = items; + pickBox.ignoreFocusOut = true; + pickBox.buttons = backBtnEnabled ? [(QuickInputButtons.Back)] : []; + return pickBox; +} diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index ab547570..0901eedd 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,7 +3,7 @@ import { commands } from "vscode"; import { Commands, executeJavaLanguageServerCommand } from "../commands"; -import { MainMethodInfo } from "../exportJarFileCommand"; +import { MainMethodInfo } from "../exportJarSteps/ResolveMainMethodStep"; import { INodeData } from "./nodeData"; export namespace Jdtls { @@ -28,7 +28,7 @@ export namespace Jdtls { } export function exportJar(mainMethod: string, elements: string[], destination: string): Thenable { - return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_EXPORTJAR, mainMethod, elements, destination); + return commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.JAVA_PROJECT_GENERATEJAR, mainMethod, elements, destination); } export enum CompileWorkspaceStatus { diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index ac5dfe51..dd845491 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -31,7 +31,7 @@ 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(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_GENERATE_JAR, (node: INodeData) => createJarFile(node))); + context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, (node: INodeData) => createJarFile(node))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => From 10f35908042f6038b172b3192e2ad466e4193d5d Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Wed, 29 Jul 2020 22:20:56 +0800 Subject: [PATCH 39/44] Divide process into steps --- src/exportJarFileCommand.ts | 52 ++++++++----------- src/exportJarSteps/IStep.ts | 12 +---- ...{GenerateJarStep.ts => StepGenerateJar.ts} | 51 +++++++++--------- ...MethodStep.ts => StepResolveMainMethod.ts} | 34 ++++++------ ...rkspaceStep.ts => StepResolveWorkspace.ts} | 35 ++++++------- src/exportJarSteps/utility.ts | 5 +- src/java/jdtls.ts | 2 +- src/views/dependencyDataProvider.ts | 2 +- 8 files changed, 84 insertions(+), 109 deletions(-) rename src/exportJarSteps/{GenerateJarStep.ts => StepGenerateJar.ts} (74%) rename src/exportJarSteps/{ResolveMainMethodStep.ts => StepResolveMainMethod.ts} (67%) rename src/exportJarSteps/{ResolveWorkspaceStep.ts => StepResolveWorkspace.ts} (54%) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 0f844df0..e38375f2 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -1,31 +1,37 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { pathExists } from "fs-extra"; import { EOL, platform } from "os"; -import { basename, extname, join } from "path"; -import { commands, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, QuickPickItem, Uri, window, workspace } from "vscode"; +import { commands, Uri, window } from "vscode"; import { sendOperationError } from "vscode-extension-telemetry-wrapper"; import { buildWorkspace } from "./build"; -import { GenerateJarStep } from "./exportJarSteps/GenerateJarStep"; -import { ExportSteps, IStep } from "./exportJarSteps/IStep"; -import { ResolveMainMethodStep } from "./exportJarSteps/ResolveMainMethodStep"; -import { ResolveWorkspaceStep } from "./exportJarSteps/ResolveWorkspaceStep"; +import { IStep } from "./exportJarSteps/IStep"; +import { StepGenerateJar } from "./exportJarSteps/StepGenerateJar"; +import { StepResolveMainMethod } from "./exportJarSteps/StepResolveMainMethod"; +import { StepResolveWorkspace } from "./exportJarSteps/StepResolveWorkspace"; import { isStandardServerReady } from "./extension"; -import { Jdtls } from "./java/jdtls"; import { INodeData } from "./java/nodeData"; let isExportingJar: boolean = false; -export class GenerateSettings { +export class StepMetadata { public entry?: INodeData; public workspaceUri?: Uri; + public isPickedWorkspace: boolean; public projectList?: INodeData[]; public selectedMainMethod?: string; public outputPath?: string; public elements: string[]; } +export namespace steps { + export const stepResolveWorkspace: StepResolveWorkspace = new StepResolveWorkspace(); + export const stepResolveMainMethod: StepResolveMainMethod = new StepResolveMainMethod(); + export const stepGenerateJar: StepGenerateJar = new StepGenerateJar(); + export let currentStep: number = 0; + export const stepsList: IStep[] = [stepResolveWorkspace, stepResolveMainMethod, stepGenerateJar]; +} + export async function createJarFile(node?: INodeData) { if (!isStandardServerReady() || isExportingJar) { return; @@ -35,31 +41,15 @@ export async function createJarFile(node?: INodeData) { if (await buildWorkspace() === false) { return reject(); } - const pickSteps: string[] = []; - let step: ExportSteps = ExportSteps.ResolveWorkspace; - const resolveWorkspaceStep: ResolveWorkspaceStep = new ResolveWorkspaceStep(); - const resolveMainMethodStep: ResolveMainMethodStep = new ResolveMainMethodStep(); - const generateJarStep: GenerateJarStep = new GenerateJarStep(); - const generateSettings: GenerateSettings = { + let step: IStep = steps.stepsList[steps.currentStep]; + const stepMetadata: StepMetadata = { entry: node, + isPickedWorkspace: false, elements: [], }; - while (step !== ExportSteps.Finish) { + while (steps.currentStep < steps.stepsList.length) { try { - switch (step) { - case ExportSteps.ResolveWorkspace: { - step = await resolveWorkspaceStep.execute(undefined, generateSettings); - break; - } - case ExportSteps.ResolveMainMethod: { - step = await resolveMainMethodStep.execute(resolveWorkspaceStep, generateSettings); - break; - } - case ExportSteps.GenerateJar: { - step = await generateJarStep.execute(resolveMainMethodStep, generateSettings); - break; - } - } + step = await step.execute(stepMetadata); } catch (err) { if (err instanceof Error) { return reject(err.message); @@ -68,7 +58,7 @@ export async function createJarFile(node?: INodeData) { } } } - resolve(generateSettings.outputPath); + resolve(stepMetadata.outputPath); }).then((message) => { successMessage(message); isExportingJar = false; diff --git a/src/exportJarSteps/IStep.ts b/src/exportJarSteps/IStep.ts index 2d318168..870237ca 100644 --- a/src/exportJarSteps/IStep.ts +++ b/src/exportJarSteps/IStep.ts @@ -1,16 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { GenerateSettings } from "../exportJarFileCommand"; +import { StepMetadata } from "../exportJarFileCommand"; export interface IStep { - exportStep: ExportSteps; - execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise; -} - -export enum ExportSteps { - ResolveWorkspace = "RESOLVEWORKSPACE", - ResolveMainMethod = "RESOLVEMAINMETHOD", - GenerateJar = "GENERATEJAR", - Finish = "FINISH", + execute(stepMetadata?: StepMetadata): Promise; } diff --git a/src/exportJarSteps/GenerateJarStep.ts b/src/exportJarSteps/StepGenerateJar.ts similarity index 74% rename from src/exportJarSteps/GenerateJarStep.ts rename to src/exportJarSteps/StepGenerateJar.ts index 5c3acd90..2e87ea7a 100644 --- a/src/exportJarSteps/GenerateJarStep.ts +++ b/src/exportJarSteps/StepGenerateJar.ts @@ -2,29 +2,26 @@ // Licensed under the MIT license. import { pathExists } from "fs-extra"; -import { EOL, platform } from "os"; import { basename, extname, join } from "path"; import { Extension, extensions, ProgressLocation, QuickInputButtons, window } from "vscode"; -import { GenerateSettings } from "../exportJarFileCommand"; +import { StepMetadata, steps } from "../exportJarFileCommand"; import { Jdtls } from "../java/jdtls"; -import { INodeData } from "../java/nodeData"; -import { ExportSteps, IStep } from "./IStep"; +import { IStep } from "./IStep"; import { createPickBox, IJarQuickPickItem } from "./utility"; -export class GenerateJarStep implements IStep { +export class StepGenerateJar implements IStep { - public exportStep: ExportSteps; - - constructor() { - this.exportStep = ExportSteps.GenerateJar; - } - - public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { - return await this.generateJar(lastStep, generateSettings) ? ExportSteps.Finish : lastStep.exportStep; + public async execute(stepMetadata: StepMetadata): Promise { + if (await this.generateJar(stepMetadata) === true) { + steps.currentStep += 1; + return undefined; + } + steps.currentStep -= 1; + return steps.stepsList[steps.currentStep]; } - private async generateJar(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { - if (await this.generateElements(lastStep, generateSettings) === false) { + private async generateJar(stepMetadata: StepMetadata): Promise { + if (await this.generateElements(stepMetadata) === false) { return false; } return window.withProgress({ @@ -36,10 +33,10 @@ export class GenerateJarStep implements IStep { token.onCancellationRequested(() => { return reject(); }); - const destPath = join(generateSettings.workspaceUri.fsPath, basename(generateSettings.workspaceUri.fsPath) + ".jar"); - const exportResult = await Jdtls.exportJar(basename(generateSettings.selectedMainMethod), generateSettings.elements, destPath); + const destPath = join(stepMetadata.workspaceUri.fsPath, basename(stepMetadata.workspaceUri.fsPath) + ".jar"); + const exportResult = await Jdtls.exportJar(basename(stepMetadata.selectedMainMethod), stepMetadata.elements, destPath); if (exportResult === true) { - generateSettings.outputPath = destPath; + stepMetadata.outputPath = destPath; resolve(true); } else { reject(new Error("Export jar failed.")); @@ -48,7 +45,7 @@ export class GenerateJarStep implements IStep { }); } - private async generateElements(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + private async generateElements(stepMetadata: StepMetadata): Promise { const extension: Extension | undefined = extensions.getExtension("redhat.java"); const extensionApi: any = await extension?.activate(); const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ @@ -62,13 +59,13 @@ export class GenerateJarStep implements IStep { }); const pickItems: IJarQuickPickItem[] = []; const uriSet: Set = new Set(); - for (const rootNode of generateSettings.projectList) { + for (const rootNode of stepMetadata.projectList) { const classPaths: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "runtime" }); - pickItems.push(...await this.parseDependencyItems(classPaths.classpaths, uriSet, generateSettings.workspaceUri.fsPath, true), - ...await this.parseDependencyItems(classPaths.modulepaths, uriSet, generateSettings.workspaceUri.fsPath, true)); + pickItems.push(...await this.parseDependencyItems(classPaths.classpaths, uriSet, stepMetadata.workspaceUri.fsPath, true), + ...await this.parseDependencyItems(classPaths.modulepaths, uriSet, stepMetadata.workspaceUri.fsPath, true)); const classPathsTest: ClasspathResult = await extensionApi.getClasspaths(rootNode.uri, { scope: "test" }); - pickItems.push(...await this.parseDependencyItems(classPathsTest.classpaths, uriSet, generateSettings.workspaceUri.fsPath, false), - ...await this.parseDependencyItems(classPathsTest.modulepaths, uriSet, generateSettings.workspaceUri.fsPath, false)); + pickItems.push(...await this.parseDependencyItems(classPathsTest.classpaths, uriSet, stepMetadata.workspaceUri.fsPath, false), + ...await this.parseDependencyItems(classPathsTest.modulepaths, uriSet, stepMetadata.workspaceUri.fsPath, false)); } resolve(pickItems); }); @@ -76,7 +73,7 @@ export class GenerateJarStep implements IStep { if (dependencyItems.length === 0) { throw new Error("No classpath found. Please make sure your project is valid."); } else if (dependencyItems.length === 1) { - generateSettings.elements.push(dependencyItems[0].uri); + stepMetadata.elements.push(dependencyItems[0].uri); return true; } dependencyItems.sort((node1, node2) => { @@ -95,7 +92,7 @@ export class GenerateJarStep implements IStep { } } return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, lastStep !== undefined, true); + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, true, true); pickBox.selectedItems = pickedDependencyItems; pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { @@ -105,7 +102,7 @@ export class GenerateJarStep implements IStep { }); pickBox.onDidAccept(() => { for (const item of pickBox.selectedItems) { - generateSettings.elements.push(item.uri); + stepMetadata.elements.push(item.uri); } resolve(true); pickBox.dispose(); diff --git a/src/exportJarSteps/ResolveMainMethodStep.ts b/src/exportJarSteps/StepResolveMainMethod.ts similarity index 67% rename from src/exportJarSteps/ResolveMainMethodStep.ts rename to src/exportJarSteps/StepResolveMainMethod.ts index 857c30ee..c367977b 100644 --- a/src/exportJarSteps/ResolveMainMethodStep.ts +++ b/src/exportJarSteps/StepResolveMainMethod.ts @@ -2,28 +2,27 @@ // Licensed under the MIT license. import { ProgressLocation, QuickInputButtons, window } from "vscode"; -import { GenerateSettings } from "../exportJarFileCommand"; +import { StepMetadata, steps } from "../exportJarFileCommand"; import { Jdtls } from "../java/jdtls"; -import { ExportSteps, IStep } from "./IStep"; +import { IStep } from "./IStep"; import { createPickBox, IJarQuickPickItem } from "./utility"; -export class ResolveMainMethodStep implements IStep { +export class StepResolveMainMethod implements IStep { private static getName(data: MainMethodInfo) { return data.name.substring(data.name.lastIndexOf(".") + 1); } - public exportStep; - - constructor() { - this.exportStep = ExportSteps.ResolveMainMethod; - } - - public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { - return await this.resolveMainMethod(lastStep, generateSettings) ? ExportSteps.GenerateJar : lastStep.exportStep; + public async execute(stepMetadata: StepMetadata): Promise { + if (await this.resolveMainMethod(stepMetadata) === true) { + steps.currentStep += 1; + } else { + steps.currentStep -= 1; + } + return steps.stepsList[steps.currentStep]; } - private async resolveMainMethod(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { + private async resolveMainMethod(stepMetadata: StepMetadata): Promise { const mainMethods: MainMethodInfo[] = await window.withProgress({ location: ProgressLocation.Window, title: "Exporting Jar : Resolving main classes...", @@ -33,17 +32,17 @@ export class ResolveMainMethodStep implements IStep { token.onCancellationRequested(() => { return reject(); }); - resolve(await Jdtls.getMainMethod(generateSettings.workspaceUri.toString())); + resolve(await Jdtls.getMainMethod(stepMetadata.workspaceUri.toString())); }); }); if (mainMethods === undefined || mainMethods.length === 0) { - generateSettings.selectedMainMethod = ""; + stepMetadata.selectedMainMethod = ""; return true; } const pickItems: IJarQuickPickItem[] = []; for (const mainMethod of mainMethods) { pickItems.push({ - label: ResolveMainMethodStep.getName(mainMethod), + label: StepResolveMainMethod.getName(mainMethod), description: mainMethod.name, }); } @@ -52,7 +51,8 @@ export class ResolveMainMethodStep implements IStep { }; pickItems.push(noMainClassItem); return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", pickItems, lastStep !== undefined); + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", + pickItems, stepMetadata.isPickedWorkspace); pickBox.onDidTriggerButton((item) => { if (item === QuickInputButtons.Back) { resolve(false); @@ -60,7 +60,7 @@ export class ResolveMainMethodStep implements IStep { } }); pickBox.onDidAccept(() => { - generateSettings.selectedMainMethod = pickBox.selectedItems[0].description; + stepMetadata.selectedMainMethod = pickBox.selectedItems[0].description; resolve(true); pickBox.dispose(); }); diff --git a/src/exportJarSteps/ResolveWorkspaceStep.ts b/src/exportJarSteps/StepResolveWorkspace.ts similarity index 54% rename from src/exportJarSteps/ResolveWorkspaceStep.ts rename to src/exportJarSteps/StepResolveWorkspace.ts index 427a097c..20d4329e 100644 --- a/src/exportJarSteps/ResolveWorkspaceStep.ts +++ b/src/exportJarSteps/StepResolveWorkspace.ts @@ -2,40 +2,34 @@ // Licensed under the MIT license. import { Uri, workspace } from "vscode"; -import { GenerateSettings } from "../exportJarFileCommand"; +import { StepMetadata, steps } from "../exportJarFileCommand"; import { Jdtls } from "../java/jdtls"; import { INodeData } from "../java/nodeData"; import { WorkspaceNode } from "../views/workspaceNode"; -import { ExportSteps, IStep } from "./IStep"; +import { IStep } from "./IStep"; import { createPickBox, IJarQuickPickItem } from "./utility"; -export class ResolveWorkspaceStep implements IStep { +export class StepResolveWorkspace implements IStep { - public exportStep; - - constructor() { - this.exportStep = ExportSteps.ResolveWorkspace; - } - - public async execute(lastStep: IStep | undefined, generateSettings: GenerateSettings): Promise { - await this.resolveWorkspaceFolder(lastStep, generateSettings, generateSettings.entry); - generateSettings.projectList = await Jdtls.getProjects(generateSettings.workspaceUri.toString()); - if (generateSettings.projectList === undefined) { + public async execute(stepMetadata: StepMetadata): Promise { + await this.resolveWorkspaceFolder(stepMetadata, stepMetadata.entry); + stepMetadata.projectList = await Jdtls.getProjects(stepMetadata.workspaceUri.toString()); + if (stepMetadata.projectList === undefined) { throw new Error("No project found. Please make sure your project folder is opened."); } - return ExportSteps.ResolveMainMethod; + steps.currentStep += 1; + return steps.stepsList[steps.currentStep]; } - private async resolveWorkspaceFolder(lastStep: IStep | undefined, generateSettings: GenerateSettings, - node?: INodeData): Promise { + private async resolveWorkspaceFolder(stepMetadata: StepMetadata, node?: INodeData): Promise { if (node instanceof WorkspaceNode) { - generateSettings.workspaceUri = Uri.parse(node.uri); + stepMetadata.workspaceUri = Uri.parse(node.uri); return true; } const folders = workspace.workspaceFolders; // Guarded by workspaceFolderCount != 0 in package.json if (folders.length === 1) { - generateSettings.workspaceUri = Uri.parse(folders[0].uri.toString()); + stepMetadata.workspaceUri = Uri.parse(folders[0].uri.toString()); return true; } const pickItems: IJarQuickPickItem[] = []; @@ -46,10 +40,11 @@ export class ResolveWorkspaceStep implements IStep { uri: folder.uri.toString(), }); } + stepMetadata.isPickedWorkspace = true; return new Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, lastStep !== undefined); + const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, false); pickBox.onDidAccept(() => { - generateSettings.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); + stepMetadata.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); resolve(true); pickBox.dispose(); }); diff --git a/src/exportJarSteps/utility.ts b/src/exportJarSteps/utility.ts index 14a31407..ebcbdfc2 100644 --- a/src/exportJarSteps/utility.ts +++ b/src/exportJarSteps/utility.ts @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { QuickInputButtons, QuickPickItem, window, QuickPick } from "vscode"; +import { QuickInputButtons, QuickPick, QuickPickItem, window } from "vscode"; +import { IStep } from "./IStep"; export interface IJarQuickPickItem extends QuickPickItem { uri?: string; @@ -9,7 +10,7 @@ export interface IJarQuickPickItem extends QuickPickItem { } export function createPickBox(title: string, placeholder: string, items: IJarQuickPickItem[], - backBtnEnabled: boolean, canSelectMany: boolean = false): QuickPick { + backBtnEnabled: boolean, canSelectMany: boolean = false): QuickPick { const pickBox = window.createQuickPick(); pickBox.title = title; pickBox.placeholder = placeholder; diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index 0901eedd..b00ceb18 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,7 +3,7 @@ import { commands } from "vscode"; import { Commands, executeJavaLanguageServerCommand } from "../commands"; -import { MainMethodInfo } from "../exportJarSteps/ResolveMainMethodStep"; +import { MainMethodInfo } from "../exportJarSteps/StepResolveMainMethod"; import { INodeData } from "./nodeData"; export namespace Jdtls { diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index dd845491..2948eb00 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -32,7 +32,7 @@ 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(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_EXPORT_JAR, (node: INodeData) => createJarFile(node))); - context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node: INodeData) => + context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node?: INodeData) => commands.executeCommand("revealFileInOS", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_FILE_PATH, (node: INodeData) => commands.executeCommand("copyFilePath", Uri.parse(node.uri)))); From 3bfc6ddc7c05dc563133a39661824c16e1df9c5a Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 30 Jul 2020 11:17:10 +0800 Subject: [PATCH 40/44] Using a map to record steps --- src/exportJarFileCommand.ts | 57 ++++++------ ...pGenerateJar.ts => GenerateJarExecutor.ts} | 77 +++++++++-------- src/exportJarSteps/IExportJarStepExecutor.ts | 14 +++ src/exportJarSteps/IStep.ts | 8 -- .../ResolveMainMethodExecutor.ts | 86 +++++++++++++++++++ ...rkspace.ts => ResolveWorkspaceExecutor.ts} | 46 ++++++---- src/exportJarSteps/StepResolveMainMethod.ts | 80 ----------------- src/exportJarSteps/utility.ts | 2 +- src/java/jdtls.ts | 2 +- 9 files changed, 201 insertions(+), 171 deletions(-) rename src/exportJarSteps/{StepGenerateJar.ts => GenerateJarExecutor.ts} (71%) create mode 100644 src/exportJarSteps/IExportJarStepExecutor.ts delete mode 100644 src/exportJarSteps/IStep.ts create mode 100644 src/exportJarSteps/ResolveMainMethodExecutor.ts rename src/exportJarSteps/{StepResolveWorkspace.ts => ResolveWorkspaceExecutor.ts} (52%) delete mode 100644 src/exportJarSteps/StepResolveMainMethod.ts diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index e38375f2..554a43da 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -5,31 +5,30 @@ import { EOL, platform } from "os"; import { commands, Uri, window } from "vscode"; import { sendOperationError } from "vscode-extension-telemetry-wrapper"; import { buildWorkspace } from "./build"; -import { IStep } from "./exportJarSteps/IStep"; -import { StepGenerateJar } from "./exportJarSteps/StepGenerateJar"; -import { StepResolveMainMethod } from "./exportJarSteps/StepResolveMainMethod"; -import { StepResolveWorkspace } from "./exportJarSteps/StepResolveWorkspace"; +import { GenerateJarExecutor } from "./exportJarSteps/GenerateJarExecutor"; +import { FinishStep, IExportJarStepExecutor } from "./exportJarSteps/IExportJarStepExecutor"; +import { ResolveMainMethodExecutor } from "./exportJarSteps/ResolveMainMethodExecutor"; +import { ResolveWorkspaceExecutor } from "./exportJarSteps/ResolveWorkspaceExecutor"; import { isStandardServerReady } from "./extension"; import { INodeData } from "./java/nodeData"; let isExportingJar: boolean = false; -export class StepMetadata { - public entry?: INodeData; - public workspaceUri?: Uri; - public isPickedWorkspace: boolean; - public projectList?: INodeData[]; - public selectedMainMethod?: string; - public outputPath?: string; - public elements: string[]; +export interface IStepMetadata { + entry?: INodeData; + workspaceUri?: Uri; + isPickedWorkspace: boolean; + projectList?: INodeData[]; + selectedMainMethod?: string; + outputPath?: string; + elements: string[]; } -export namespace steps { - export const stepResolveWorkspace: StepResolveWorkspace = new StepResolveWorkspace(); - export const stepResolveMainMethod: StepResolveMainMethod = new StepResolveMainMethod(); - export const stepGenerateJar: StepGenerateJar = new StepGenerateJar(); - export let currentStep: number = 0; - export const stepsList: IStep[] = [stepResolveWorkspace, stepResolveMainMethod, stepGenerateJar]; +export enum ExportJarStep { + ResolveWorkspace = "RESOLVEWORKSPACE", + ResolveMainMethod = "RESOLVEMAINMETHOD", + GenerateJar = "GENERATEJAR", + Finish = "FINISH", } export async function createJarFile(node?: INodeData) { @@ -37,28 +36,32 @@ export async function createJarFile(node?: INodeData) { return; } isExportingJar = true; + const stepMap: Map = new Map(); + stepMap.set(ExportJarStep.ResolveWorkspace, new ResolveWorkspaceExecutor()); + stepMap.set(ExportJarStep.ResolveMainMethod, new ResolveMainMethodExecutor()); + stepMap.set(ExportJarStep.GenerateJar, new GenerateJarExecutor()); + stepMap.set(ExportJarStep.Finish, new FinishStep()); return new Promise(async (resolve, reject) => { if (await buildWorkspace() === false) { return reject(); } - let step: IStep = steps.stepsList[steps.currentStep]; - const stepMetadata: StepMetadata = { + let step: ExportJarStep = ExportJarStep.ResolveWorkspace; + const stepMetadata: IStepMetadata = { entry: node, isPickedWorkspace: false, elements: [], }; - while (steps.currentStep < steps.stepsList.length) { + while (step !== ExportJarStep.Finish) { try { - step = await step.execute(stepMetadata); + step = await stepMap.get(step).execute(stepMetadata); } catch (err) { - if (err instanceof Error) { - return reject(err.message); - } else { - return reject(err); + if (err === undefined) { + return reject(); } + return reject(`${err}`); } } - resolve(stepMetadata.outputPath); + return resolve(stepMetadata.outputPath); }).then((message) => { successMessage(message); isExportingJar = false; diff --git a/src/exportJarSteps/StepGenerateJar.ts b/src/exportJarSteps/GenerateJarExecutor.ts similarity index 71% rename from src/exportJarSteps/StepGenerateJar.ts rename to src/exportJarSteps/GenerateJarExecutor.ts index 2e87ea7a..55adc746 100644 --- a/src/exportJarSteps/StepGenerateJar.ts +++ b/src/exportJarSteps/GenerateJarExecutor.ts @@ -3,25 +3,23 @@ import { pathExists } from "fs-extra"; import { basename, extname, join } from "path"; -import { Extension, extensions, ProgressLocation, QuickInputButtons, window } from "vscode"; -import { StepMetadata, steps } from "../exportJarFileCommand"; +import { Disposable, Extension, extensions, ProgressLocation, QuickInputButtons, QuickPick, window } from "vscode"; +import { ExportJarStep, IStepMetadata } from "../exportJarFileCommand"; import { Jdtls } from "../java/jdtls"; -import { IStep } from "./IStep"; +import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; import { createPickBox, IJarQuickPickItem } from "./utility"; -export class StepGenerateJar implements IStep { +export class GenerateJarExecutor implements IExportJarStepExecutor { - public async execute(stepMetadata: StepMetadata): Promise { - if (await this.generateJar(stepMetadata) === true) { - steps.currentStep += 1; - return undefined; + public async execute(stepMetadata: IStepMetadata): Promise { + if (await this.generateJar(stepMetadata)) { + return ExportJarStep.Finish; } - steps.currentStep -= 1; - return steps.stepsList[steps.currentStep]; + return ExportJarStep.ResolveMainMethod; } - private async generateJar(stepMetadata: StepMetadata): Promise { - if (await this.generateElements(stepMetadata) === false) { + private async generateJar(stepMetadata: IStepMetadata): Promise { + if (!(await this.generateElements(stepMetadata))) { return false; } return window.withProgress({ @@ -37,15 +35,15 @@ export class StepGenerateJar implements IStep { const exportResult = await Jdtls.exportJar(basename(stepMetadata.selectedMainMethod), stepMetadata.elements, destPath); if (exportResult === true) { stepMetadata.outputPath = destPath; - resolve(true); + return resolve(true); } else { - reject(new Error("Export jar failed.")); + return reject(new Error("Export jar failed.")); } }); }); } - private async generateElements(stepMetadata: StepMetadata): Promise { + private async generateElements(stepMetadata: IStepMetadata): Promise { const extension: Extension | undefined = extensions.getExtension("redhat.java"); const extensionApi: any = await extension?.activate(); const dependencyItems: IJarQuickPickItem[] = await window.withProgress({ @@ -67,7 +65,7 @@ export class StepGenerateJar implements IStep { pickItems.push(...await this.parseDependencyItems(classPathsTest.classpaths, uriSet, stepMetadata.workspaceUri.fsPath, false), ...await this.parseDependencyItems(classPathsTest.modulepaths, uriSet, stepMetadata.workspaceUri.fsPath, false)); } - resolve(pickItems); + return resolve(pickItems); }); }); if (dependencyItems.length === 0) { @@ -91,28 +89,37 @@ export class StepGenerateJar implements IStep { pickedDependencyItems.push(item); } } - return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, true, true); + const disposables: Disposable[] = []; + let pickBox: QuickPick; + const result = await new Promise(async (resolve, reject) => { + pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, true, true); pickBox.selectedItems = pickedDependencyItems; - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - resolve(false); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - for (const item of pickBox.selectedItems) { - stepMetadata.elements.push(item.uri); - } - resolve(true); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); + disposables.push( + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + return resolve(false); + } + }), + pickBox.onDidAccept(() => { + for (const item of pickBox.selectedItems) { + stepMetadata.elements.push(item.uri); + } + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); + disposables.push(pickBox); pickBox.show(); }); + for (const d of disposables) { + d.dispose(); + } + if (pickBox !== undefined) { + pickBox.dispose(); + } + return result; } private async parseDependencyItems(paths: string[], uriSet: Set, projectPath: string, isRuntime: boolean): Promise { diff --git a/src/exportJarSteps/IExportJarStepExecutor.ts b/src/exportJarSteps/IExportJarStepExecutor.ts new file mode 100644 index 00000000..2ba326da --- /dev/null +++ b/src/exportJarSteps/IExportJarStepExecutor.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { ExportJarStep, IStepMetadata } from "../exportJarFileCommand"; + +export interface IExportJarStepExecutor { + execute(stepMetadata?: IStepMetadata): Promise; +} + +export class FinishStep implements IExportJarStepExecutor { + public async execute(): Promise { + return ExportJarStep.Finish; + } +} diff --git a/src/exportJarSteps/IStep.ts b/src/exportJarSteps/IStep.ts deleted file mode 100644 index 870237ca..00000000 --- a/src/exportJarSteps/IStep.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { StepMetadata } from "../exportJarFileCommand"; - -export interface IStep { - execute(stepMetadata?: StepMetadata): Promise; -} diff --git a/src/exportJarSteps/ResolveMainMethodExecutor.ts b/src/exportJarSteps/ResolveMainMethodExecutor.ts new file mode 100644 index 00000000..22f34855 --- /dev/null +++ b/src/exportJarSteps/ResolveMainMethodExecutor.ts @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Disposable, ProgressLocation, QuickInputButtons, QuickPick, window } from "vscode"; +import { ExportJarStep, IStepMetadata } from "../exportJarFileCommand"; +import { Jdtls } from "../java/jdtls"; +import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; +import { createPickBox, IJarQuickPickItem } from "./utility"; + +export class ResolveMainMethodExecutor implements IExportJarStepExecutor { + + private static getName(data: MainMethodInfo) { + return data.name.substring(data.name.lastIndexOf(".") + 1); + } + + public async execute(stepMetadata: IStepMetadata): Promise { + if (await this.resolveMainMethod(stepMetadata)) { + return ExportJarStep.GenerateJar; + } + return ExportJarStep.ResolveWorkspace; + } + + private async resolveMainMethod(stepMetadata: IStepMetadata): Promise { + const mainMethods: MainMethodInfo[] = await window.withProgress({ + location: ProgressLocation.Window, + title: "Exporting Jar : Resolving main classes...", + cancellable: true, + }, (progress, token) => { + return new Promise(async (resolve, reject) => { + token.onCancellationRequested(() => { + return reject(); + }); + resolve(await Jdtls.getMainMethod(stepMetadata.workspaceUri.toString())); + }); + }); + if (mainMethods === undefined || mainMethods.length === 0) { + stepMetadata.selectedMainMethod = ""; + return true; + } + const pickItems: IJarQuickPickItem[] = []; + for (const mainMethod of mainMethods) { + pickItems.push({ + label: ResolveMainMethodExecutor.getName(mainMethod), + description: mainMethod.name, + }); + } + const noMainClassItem: IJarQuickPickItem = { + label: "", + }; + pickItems.push(noMainClassItem); + const disposables: Disposable[] = []; + let pickBox: QuickPick; + const result = await new Promise(async (resolve, reject) => { + pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", + pickItems, stepMetadata.isPickedWorkspace); + disposables.push( + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + return resolve(false); + } + }), + pickBox.onDidAccept(() => { + stepMetadata.selectedMainMethod = pickBox.selectedItems[0].description; + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); + pickBox.show(); + }); + for (const d of disposables) { + d.dispose(); + } + if (pickBox !== undefined) { + pickBox.dispose(); + } + return result; + } + +} + +export class MainMethodInfo { + public name: string; + public path: string; +} diff --git a/src/exportJarSteps/StepResolveWorkspace.ts b/src/exportJarSteps/ResolveWorkspaceExecutor.ts similarity index 52% rename from src/exportJarSteps/StepResolveWorkspace.ts rename to src/exportJarSteps/ResolveWorkspaceExecutor.ts index 20d4329e..208df67c 100644 --- a/src/exportJarSteps/StepResolveWorkspace.ts +++ b/src/exportJarSteps/ResolveWorkspaceExecutor.ts @@ -1,27 +1,26 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -import { Uri, workspace } from "vscode"; -import { StepMetadata, steps } from "../exportJarFileCommand"; +import { Disposable, QuickPick, Uri, workspace } from "vscode"; +import { ExportJarStep, IStepMetadata } from "../exportJarFileCommand"; import { Jdtls } from "../java/jdtls"; import { INodeData } from "../java/nodeData"; import { WorkspaceNode } from "../views/workspaceNode"; -import { IStep } from "./IStep"; +import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; import { createPickBox, IJarQuickPickItem } from "./utility"; -export class StepResolveWorkspace implements IStep { +export class ResolveWorkspaceExecutor implements IExportJarStepExecutor { - public async execute(stepMetadata: StepMetadata): Promise { + public async execute(stepMetadata: IStepMetadata): Promise { await this.resolveWorkspaceFolder(stepMetadata, stepMetadata.entry); stepMetadata.projectList = await Jdtls.getProjects(stepMetadata.workspaceUri.toString()); if (stepMetadata.projectList === undefined) { throw new Error("No project found. Please make sure your project folder is opened."); } - steps.currentStep += 1; - return steps.stepsList[steps.currentStep]; + return ExportJarStep.ResolveMainMethod; } - private async resolveWorkspaceFolder(stepMetadata: StepMetadata, node?: INodeData): Promise { + private async resolveWorkspaceFolder(stepMetadata: IStepMetadata, node?: INodeData): Promise { if (node instanceof WorkspaceNode) { stepMetadata.workspaceUri = Uri.parse(node.uri); return true; @@ -41,18 +40,27 @@ export class StepResolveWorkspace implements IStep { }); } stepMetadata.isPickedWorkspace = true; - return new Promise((resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, false); - pickBox.onDidAccept(() => { - stepMetadata.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); - resolve(true); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); + const disposables: Disposable[] = []; + let pickBox: QuickPick; + const result = await new Promise((resolve, reject) => { + pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, false); + disposables.push( + pickBox.onDidAccept(() => { + stepMetadata.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); pickBox.show(); }); + for (const d of disposables) { + d.dispose(); + } + if (pickBox !== undefined) { + pickBox.dispose(); + } + return result; } } diff --git a/src/exportJarSteps/StepResolveMainMethod.ts b/src/exportJarSteps/StepResolveMainMethod.ts deleted file mode 100644 index c367977b..00000000 --- a/src/exportJarSteps/StepResolveMainMethod.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { ProgressLocation, QuickInputButtons, window } from "vscode"; -import { StepMetadata, steps } from "../exportJarFileCommand"; -import { Jdtls } from "../java/jdtls"; -import { IStep } from "./IStep"; -import { createPickBox, IJarQuickPickItem } from "./utility"; - -export class StepResolveMainMethod implements IStep { - - private static getName(data: MainMethodInfo) { - return data.name.substring(data.name.lastIndexOf(".") + 1); - } - - public async execute(stepMetadata: StepMetadata): Promise { - if (await this.resolveMainMethod(stepMetadata) === true) { - steps.currentStep += 1; - } else { - steps.currentStep -= 1; - } - return steps.stepsList[steps.currentStep]; - } - - private async resolveMainMethod(stepMetadata: StepMetadata): Promise { - const mainMethods: MainMethodInfo[] = await window.withProgress({ - location: ProgressLocation.Window, - title: "Exporting Jar : Resolving main classes...", - cancellable: true, - }, (progress, token) => { - return new Promise(async (resolve, reject) => { - token.onCancellationRequested(() => { - return reject(); - }); - resolve(await Jdtls.getMainMethod(stepMetadata.workspaceUri.toString())); - }); - }); - if (mainMethods === undefined || mainMethods.length === 0) { - stepMetadata.selectedMainMethod = ""; - return true; - } - const pickItems: IJarQuickPickItem[] = []; - for (const mainMethod of mainMethods) { - pickItems.push({ - label: StepResolveMainMethod.getName(mainMethod), - description: mainMethod.name, - }); - } - const noMainClassItem: IJarQuickPickItem = { - label: "", - }; - pickItems.push(noMainClassItem); - return new Promise(async (resolve, reject) => { - const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", - pickItems, stepMetadata.isPickedWorkspace); - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - resolve(false); - pickBox.dispose(); - } - }); - pickBox.onDidAccept(() => { - stepMetadata.selectedMainMethod = pickBox.selectedItems[0].description; - resolve(true); - pickBox.dispose(); - }); - pickBox.onDidHide(() => { - reject(); - pickBox.dispose(); - }); - pickBox.show(); - }); - } - -} - -export class MainMethodInfo { - public name: string; - public path: string; -} diff --git a/src/exportJarSteps/utility.ts b/src/exportJarSteps/utility.ts index ebcbdfc2..0cc4d627 100644 --- a/src/exportJarSteps/utility.ts +++ b/src/exportJarSteps/utility.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import { QuickInputButtons, QuickPick, QuickPickItem, window } from "vscode"; -import { IStep } from "./IStep"; +import { IExportJarStepExecutor } from "./IExportJarStepExecutor"; export interface IJarQuickPickItem extends QuickPickItem { uri?: string; diff --git a/src/java/jdtls.ts b/src/java/jdtls.ts index b00ceb18..cd29bfcc 100644 --- a/src/java/jdtls.ts +++ b/src/java/jdtls.ts @@ -3,7 +3,7 @@ import { commands } from "vscode"; import { Commands, executeJavaLanguageServerCommand } from "../commands"; -import { MainMethodInfo } from "../exportJarSteps/StepResolveMainMethod"; +import { MainMethodInfo } from "../exportJarSteps/ResolveMainMethodExecutor"; import { INodeData } from "./nodeData"; export namespace Jdtls { From c31e7c1dc09fee5b6c46d53fabcc655c1928693d Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:02:13 +0800 Subject: [PATCH 41/44] Apply changes 1.Fix disposables bugs 2.move stepMap to grobal --- src/exportJarFileCommand.ts | 20 +++---- src/exportJarSteps/GenerateJarExecutor.ts | 56 +++++++++---------- .../ResolveMainMethodExecutor.ts | 52 ++++++++--------- .../ResolveWorkspaceExecutor.ts | 39 ++++++------- 4 files changed, 83 insertions(+), 84 deletions(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 554a43da..7e9b288c 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -12,8 +12,6 @@ import { ResolveWorkspaceExecutor } from "./exportJarSteps/ResolveWorkspaceExecu import { isStandardServerReady } from "./extension"; import { INodeData } from "./java/nodeData"; -let isExportingJar: boolean = false; - export interface IStepMetadata { entry?: INodeData; workspaceUri?: Uri; @@ -31,16 +29,19 @@ export enum ExportJarStep { Finish = "FINISH", } +let isExportingJar: boolean = false; +const stepMap: Map = new Map([ + [ExportJarStep.ResolveWorkspace, new ResolveWorkspaceExecutor()], + [ExportJarStep.ResolveMainMethod, new ResolveMainMethodExecutor()], + [ExportJarStep.GenerateJar, new GenerateJarExecutor()], + [ExportJarStep.Finish, new FinishStep()], +]); + export async function createJarFile(node?: INodeData) { if (!isStandardServerReady() || isExportingJar) { return; } isExportingJar = true; - const stepMap: Map = new Map(); - stepMap.set(ExportJarStep.ResolveWorkspace, new ResolveWorkspaceExecutor()); - stepMap.set(ExportJarStep.ResolveMainMethod, new ResolveMainMethodExecutor()); - stepMap.set(ExportJarStep.GenerateJar, new GenerateJarExecutor()); - stepMap.set(ExportJarStep.Finish, new FinishStep()); return new Promise(async (resolve, reject) => { if (await buildWorkspace() === false) { return reject(); @@ -55,10 +56,7 @@ export async function createJarFile(node?: INodeData) { try { step = await stepMap.get(step).execute(stepMetadata); } catch (err) { - if (err === undefined) { - return reject(); - } - return reject(`${err}`); + return err ? reject(`${err}`) : reject(); } } return resolve(stepMetadata.outputPath); diff --git a/src/exportJarSteps/GenerateJarExecutor.ts b/src/exportJarSteps/GenerateJarExecutor.ts index 55adc746..c75357bf 100644 --- a/src/exportJarSteps/GenerateJarExecutor.ts +++ b/src/exportJarSteps/GenerateJarExecutor.ts @@ -90,34 +90,34 @@ export class GenerateJarExecutor implements IExportJarStepExecutor { } } const disposables: Disposable[] = []; - let pickBox: QuickPick; - const result = await new Promise(async (resolve, reject) => { - pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, true, true); - pickBox.selectedItems = pickedDependencyItems; - disposables.push( - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - return resolve(false); - } - }), - pickBox.onDidAccept(() => { - for (const item of pickBox.selectedItems) { - stepMetadata.elements.push(item.uri); - } - return resolve(true); - }), - pickBox.onDidHide(() => { - return reject(); - }), - ); - disposables.push(pickBox); - pickBox.show(); - }); - for (const d of disposables) { - d.dispose(); - } - if (pickBox !== undefined) { - pickBox.dispose(); + let result: boolean = false; + try { + result = await new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine elements", "Select the elements", dependencyItems, true, true); + pickBox.selectedItems = pickedDependencyItems; + disposables.push( + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + return resolve(false); + } + }), + pickBox.onDidAccept(() => { + for (const item of pickBox.selectedItems) { + stepMetadata.elements.push(item.uri); + } + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); + disposables.push(pickBox); + pickBox.show(); + }); + } finally { + for (const d of disposables) { + d.dispose(); + } } return result; } diff --git a/src/exportJarSteps/ResolveMainMethodExecutor.ts b/src/exportJarSteps/ResolveMainMethodExecutor.ts index 22f34855..8cd6d8cd 100644 --- a/src/exportJarSteps/ResolveMainMethodExecutor.ts +++ b/src/exportJarSteps/ResolveMainMethodExecutor.ts @@ -49,35 +49,35 @@ export class ResolveMainMethodExecutor implements IExportJarStepExecutor { }; pickItems.push(noMainClassItem); const disposables: Disposable[] = []; - let pickBox: QuickPick; - const result = await new Promise(async (resolve, reject) => { - pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", - pickItems, stepMetadata.isPickedWorkspace); - disposables.push( - pickBox.onDidTriggerButton((item) => { - if (item === QuickInputButtons.Back) { - return resolve(false); - } - }), - pickBox.onDidAccept(() => { - stepMetadata.selectedMainMethod = pickBox.selectedItems[0].description; - return resolve(true); - }), - pickBox.onDidHide(() => { - return reject(); - }), - ); - pickBox.show(); - }); - for (const d of disposables) { - d.dispose(); - } - if (pickBox !== undefined) { - pickBox.dispose(); + let result: boolean = false; + try { + result = await new Promise(async (resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine main class", "Select the main class", + pickItems, stepMetadata.isPickedWorkspace); + disposables.push( + pickBox.onDidTriggerButton((item) => { + if (item === QuickInputButtons.Back) { + return resolve(false); + } + }), + pickBox.onDidAccept(() => { + stepMetadata.selectedMainMethod = pickBox.selectedItems[0].description; + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); + disposables.push(pickBox); + pickBox.show(); + }); + } finally { + for (const d of disposables) { + d.dispose(); + } } return result; } - } export class MainMethodInfo { diff --git a/src/exportJarSteps/ResolveWorkspaceExecutor.ts b/src/exportJarSteps/ResolveWorkspaceExecutor.ts index 208df67c..7be4f968 100644 --- a/src/exportJarSteps/ResolveWorkspaceExecutor.ts +++ b/src/exportJarSteps/ResolveWorkspaceExecutor.ts @@ -41,25 +41,26 @@ export class ResolveWorkspaceExecutor implements IExportJarStepExecutor { } stepMetadata.isPickedWorkspace = true; const disposables: Disposable[] = []; - let pickBox: QuickPick; - const result = await new Promise((resolve, reject) => { - pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, false); - disposables.push( - pickBox.onDidAccept(() => { - stepMetadata.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); - return resolve(true); - }), - pickBox.onDidHide(() => { - return reject(); - }), - ); - pickBox.show(); - }); - for (const d of disposables) { - d.dispose(); - } - if (pickBox !== undefined) { - pickBox.dispose(); + let result: boolean = false; + try { + result = await new Promise((resolve, reject) => { + const pickBox = createPickBox("Export Jar : Determine project", "Select the project", pickItems, false); + disposables.push( + pickBox.onDidAccept(() => { + stepMetadata.workspaceUri = Uri.parse(pickBox.selectedItems[0].uri); + return resolve(true); + }), + pickBox.onDidHide(() => { + return reject(); + }), + ); + disposables.push(pickBox); + pickBox.show(); + }); + } finally { + for (const d of disposables) { + d.dispose(); + } } return result; } From 720103ac08505c42a701c6048d852d0990f61b03 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:08:43 +0800 Subject: [PATCH 42/44] Delete unnecessary code --- src/exportJarFileCommand.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 7e9b288c..72cef233 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -34,7 +34,6 @@ const stepMap: Map = new Map Date: Thu, 30 Jul 2020 12:23:22 +0800 Subject: [PATCH 43/44] Delete unnecessary code --- src/exportJarSteps/IExportJarStepExecutor.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/exportJarSteps/IExportJarStepExecutor.ts b/src/exportJarSteps/IExportJarStepExecutor.ts index 2ba326da..fee4d3d4 100644 --- a/src/exportJarSteps/IExportJarStepExecutor.ts +++ b/src/exportJarSteps/IExportJarStepExecutor.ts @@ -6,9 +6,3 @@ import { ExportJarStep, IStepMetadata } from "../exportJarFileCommand"; export interface IExportJarStepExecutor { execute(stepMetadata?: IStepMetadata): Promise; } - -export class FinishStep implements IExportJarStepExecutor { - public async execute(): Promise { - return ExportJarStep.Finish; - } -} From 1381e33c987b714952ebf9be1307943f403deac2 Mon Sep 17 00:00:00 2001 From: CS <45906942+CsCherrYY@users.noreply.github.com> Date: Thu, 30 Jul 2020 12:32:43 +0800 Subject: [PATCH 44/44] Delete unnecessary code fix --- src/exportJarFileCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exportJarFileCommand.ts b/src/exportJarFileCommand.ts index 72cef233..846e717a 100644 --- a/src/exportJarFileCommand.ts +++ b/src/exportJarFileCommand.ts @@ -6,7 +6,7 @@ import { commands, Uri, window } from "vscode"; import { sendOperationError } from "vscode-extension-telemetry-wrapper"; import { buildWorkspace } from "./build"; import { GenerateJarExecutor } from "./exportJarSteps/GenerateJarExecutor"; -import { FinishStep, IExportJarStepExecutor } from "./exportJarSteps/IExportJarStepExecutor"; +import { IExportJarStepExecutor } from "./exportJarSteps/IExportJarStepExecutor"; import { ResolveMainMethodExecutor } from "./exportJarSteps/ResolveMainMethodExecutor"; import { ResolveWorkspaceExecutor } from "./exportJarSteps/ResolveWorkspaceExecutor"; import { isStandardServerReady } from "./extension";