Implement resolve classpath command

This commit is contained in:
nsingh
2018-03-08 15:18:42 -08:00
parent 319bc0124b
commit a305e2ce53
17 changed files with 318 additions and 26 deletions

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver;
public class ClasspathParams {
private String resourceUri;
public ClasspathParams(String resourceUri) {
super();
this.resourceUri = resourceUri;
}
public String getResourceUri() {
return resourceUri;
}
public void setResourceUri(String resourceUri) {
this.resourceUri = resourceUri;
}
@Override
public String toString() {
return "ClasspathParams [resourceUri=" + resourceUri + "]";
}
}

View File

@@ -34,4 +34,7 @@ public interface STS4LanguageClient extends LanguageClient {
@JsonRequest("sts/moveCursor")
CompletableFuture<Object> moveCursor(CursorMovement cursorMovement);
@JsonRequest("sts/classpath")
CompletableFuture<Object> classpath(ClasspathParams classpathEvent);
}

View File

@@ -16,6 +16,7 @@ import java.nio.file.Path;
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache;
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.util.Log;
@@ -30,20 +31,20 @@ public class MavenJavaProject extends AbstractJavaProject {
private DelegatingCachedClasspath<MavenProjectClasspath> classpath;
private File pom;
public MavenJavaProject(MavenCore maven, File pom, Path projectDataCache) {
public MavenJavaProject(STS4LanguageClient client, MavenCore maven, File pom, Path projectDataCache) {
super(projectDataCache);
this.pom = pom;
File file = projectDataCache == null ? null
: projectDataCache.resolve(ClasspathFileBasedCache.CLASSPATH_DATA_CACHE_FILE).toFile();
ClasspathFileBasedCache fileBasedCache = new ClasspathFileBasedCache(file);
this.classpath = new DelegatingCachedClasspath<>(
() -> new MavenProjectClasspath(maven, pom),
() -> new MavenProjectClasspath(client, maven, pom),
fileBasedCache
);
}
public MavenJavaProject(MavenCore maven, File pom) {
this(maven, pom, null);
public MavenJavaProject(STS4LanguageClient client, MavenCore maven, File pom) {
this(client, maven, pom, null);
if (!classpath.isCached()) {
try {
classpath.update();

View File

@@ -15,9 +15,11 @@ import java.nio.file.Path;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.MessageType;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.maven.MavenCore;
/**
@@ -47,11 +49,18 @@ public class MavenProjectCache extends AbstractFileToProjectCache<MavenJavaProje
@Override
protected MavenJavaProject createProject(File pomFile) throws Exception {
MavenJavaProject mavenJavaProject = new MavenJavaProject(maven, pomFile,
MavenJavaProject mavenJavaProject = new MavenJavaProject(
getClient(),
maven,
pomFile,
projectCacheFolder == null ? null : pomFile.getParentFile().toPath().resolve(projectCacheFolder)
);
performUpdate(mavenJavaProject, asyncUpdate, asyncUpdate);
return mavenJavaProject;
}
private STS4LanguageClient getClient() {
return ((SimpleLanguageServer) server).getClient();
}
}

View File

@@ -33,6 +33,8 @@ import org.springframework.ide.vscode.commons.java.ClasspathData;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.maven.MavenException;
import org.springframework.ide.vscode.commons.util.Log;
@@ -51,9 +53,11 @@ public class MavenProjectClasspath extends JandexClasspath {
private MavenCore maven;
private File pom;
private MavenClasspathData cachedData;
private STS4LanguageClient client;
MavenProjectClasspath(MavenCore maven, File pom) throws Exception {
MavenProjectClasspath(STS4LanguageClient client, MavenCore maven, File pom) throws Exception {
super();
this.client = client;
this.maven = maven;
this.pom = pom;
this.cachedData = createClasspathData();
@@ -94,6 +98,7 @@ public class MavenProjectClasspath extends JandexClasspath {
// return Stream.concat(maven.resolveDependencies(project, null).stream().map(artifact -> {
// return artifact.getFile().toPath();
// }), projectResolvedOutput());
Object classpath = client.classpath(new ClasspathParams(pom.toURI().toString())).get();
ImmutableList<Path> classpathEntries = ImmutableList.copyOf(Stream.concat(projectDependencies(project).stream().map(a -> a.getFile().toPath()),
projectOutput(project).stream().map(f -> f.toPath())).collect(Collectors.toList()));
return classpathEntries;

View File

@@ -40,7 +40,7 @@ public class HtmlJavadocTest {
JandexClasspath.providerType = JavadocProviderTypes.HTML;
testProjectPath = Paths.get(HtmlJavadocTest.class.getResource("/gs-rest-service-cors-boot-1.4.1-with-classpath-file").toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
} catch (Exception e) {
return null;
}

View File

@@ -47,7 +47,7 @@ public class JavaIndexTest {
public MavenJavaProject load(String projectName) throws Exception {
Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/" + projectName).toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
}
});

View File

@@ -90,6 +90,7 @@ import org.eclipse.lsp4j.WorkspaceClientCapabilities;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.HighlightParams;
import org.springframework.ide.vscode.commons.languageserver.ProgressParams;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
@@ -292,6 +293,11 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false));
}
@Override
public CompletableFuture<Object> classpath(ClasspathParams classpathEvent) {
return CompletableFuture.completedFuture(null);
}
});
}

View File

@@ -6,4 +6,6 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: Pivotal Inc.
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.eclipse.jdt.ls.core,
org.eclipse.core.runtime
org.eclipse.core.runtime,
org.eclipse.jdt.core,
org.eclipse.core.resources

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.jdt.ls.extension;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class Logger {
private static PrintWriter printwriter;
static {
File file = new File(System.getProperty("java.io.tmpdir"));
file = new File(file, "stsjdt.log");
try {
printwriter = new PrintWriter(new FileOutputStream(file), true);
log("=====================================");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void log(String message) {
printwriter.println(message);
printwriter.flush();
}
public static void log(Exception e) {
e.printStackTrace(printwriter);
}
}

View File

@@ -10,17 +10,181 @@
*******************************************************************************/
package org.springframework.tooling.jdt.ls.extension;
import java.util.List;
import static org.springframework.tooling.jdt.ls.extension.Logger.log;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
@SuppressWarnings("restriction")
public class ResolveClasspathHandler implements IDelegateCommandHandler {
public static final String ENTRY_CONTENT_SOURCE = "source";
public static final String ENTRY_CONTENT_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
return "Tasty "+arguments.get(0)+"!";
try {
URI resourceUri = getResourceUri(arguments);
IJavaProject javaProject = getJavaProject(resourceUri);
return resolveClasspathHandler(javaProject);
} catch (Exception e) {
log(e);
throw e;
}
}
private Classpath resolveClasspathHandler(IJavaProject javaProject) throws Exception {
List<CPE> cpEntries = new ArrayList<>();
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
if (entries != null) {
for (IClasspathEntry entry : entries) {
String kind = toContentKind(entry);
String path = entry.getPath().toString();
cpEntries.add(new CPE(kind, path));
}
}
Classpath classpath = new Classpath(cpEntries, javaProject.getOutputLocation().toString());
log("classpath=" + classpath);
return classpath;
}
private String toContentKind(IClasspathEntry entry) {
switch (entry.getContentKind()) {
case IPackageFragmentRoot.K_BINARY:
return ENTRY_CONTENT_BINARY;
case IPackageFragmentRoot.K_SOURCE:
return ENTRY_CONTENT_SOURCE;
default:
return "unknown: " + entry.getContentKind();
}
}
private IJavaProject getJavaProject(URI resourceUri) throws Exception {
IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(resourceUri);
log("containers=" + containers);
if (containers.length > 0) {
log("containers.length=" + containers.length);
Optional<IContainer> shortest = Arrays.stream(containers)
.min((f1, f2) -> f1.getFullPath().segmentCount() - f2.getFullPath().segmentCount());
log("shortest=" + shortest.isPresent());
if (shortest.isPresent()) {
log("shortest.fullpath=" + shortest.get().getFullPath());
IProject project = shortest.get().getProject();
log("project=" + project.getName());
if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) {
IJavaProject javaProject = JavaCore.create(project);
log("javaProject=" + javaProject.getElementName());
return javaProject;
}
}
}
throw new Exception("Resolve classpath: unable to resolve a Java project from : " + resourceUri);
}
private URI getResourceUri(List<Object> arguments) throws Exception {
if (arguments == null || arguments.size() == 0) {
throw new IllegalArgumentException(
"Resolve classpath: Invalid number of arguments. A resource URI is required.");
}
URI uri = URI.create((String) arguments.get(0));
File resource = new File(uri);
if (!resource.exists()) {
throw new IllegalArgumentException("Resolve classpath: Resource does not exist: " + resource);
}
return uri;
}
public static class CPE {
private String kind;
private String path;
public CPE(String kind, String path) {
super();
this.kind = kind;
this.path = path;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "CPE [kind=" + kind + ", path=" + path + "]\n";
}
}
public static class Classpath {
private List<CPE> entries;
private String defaultOutputFolder;
public Classpath(List<CPE> entries, String defaultOutputFolder) {
super();
this.entries = entries;
this.defaultOutputFolder = defaultOutputFolder;
}
public List<CPE> getEntries() {
return entries;
}
public void setEntries(List<CPE> entries) {
this.entries = entries;
}
public String getDefaultOutputFolder() {
return defaultOutputFolder;
}
public void setDefaultOutputFolder(String defaultOutputFolder) {
this.defaultOutputFolder = defaultOutputFolder;
}
@Override
public String toString() {
return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]";
}
}
}

View File

@@ -43,7 +43,7 @@ public class VSCodeSourceLinksTest {
public MavenJavaProject load(String projectName) throws Exception {
Path testProjectPath = Paths.get(VSCodeSourceLinksTest.class.getResource("/test-projects/" + projectName).toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
}
});

View File

@@ -101,7 +101,7 @@ public class ProjectsHarness {
switch (type) {
case MAVEN:
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
case CLASSPATH_TXT:
MavenBuilder.newBuilder(testProjectPath).clean().pack().skipTests().execute();
return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile());

View File

@@ -0,0 +1,31 @@
'use strict';
import * as VSCode from 'vscode';
import { LanguageClient, RequestType } from 'vscode-languageclient';
export function registerClasspathService(client : LanguageClient) : void {
let classpathRequest = new RequestType<ClasspathParams, ClasspathResponse, void, void>("sts/classpath");
client.onRequest(classpathRequest, async (params: ClasspathParams) => {
return await executeClasspathCommand(params.resourceUri);
});
}
async function executeClasspathCommand(resourceUri : string) : Promise<ClasspathResponse> {
return <ClasspathResponse> (await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.resolveClasspath", resourceUri));
}
interface ClasspathResponse {
entries: ClasspathEntry[],
defaultOutputFolder : string
}
interface ClasspathParams {
resourceUri: string
}
interface ClasspathEntry {
kind : string,
path : string
}

View File

@@ -1,8 +1,4 @@
import * as code from 'vscode';
'use strict';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as VSCode from 'vscode';
import * as Path from 'path';
@@ -19,6 +15,7 @@ import {HighlightService, HighlightParams} from './highlight-service';
import { log } from 'util';
import { tmpdir } from 'os';
import { JVM, findJvm, findJdk } from '@pivotal-tools/jvm-launch-utils';
import { registerClasspathService } from './classpath';
let p2c = P2C.createConverter();
@@ -199,6 +196,7 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser
context.subscriptions.push(disposable);
context.subscriptions.push(progressService);
context.subscriptions.push(highlightService);
return client.onReady().then(() => {
client.onNotification(progressNotification, (params: ProgressParams) => {
progressService.handle(params);
@@ -217,6 +215,7 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser
}
return { applied: true};
});
registerClasspathService(client);
return client;
});
}

View File

@@ -0,0 +1,4 @@
#!/bin/bash
set -e -x
npm install
npm run vsce-package

View File

@@ -38,15 +38,6 @@ export function activate(context: VSCode.ExtensionContext) {
}
}
};
setTimeout(() => {
VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.resolveClasspath", "Brocoli", "Cabbage")
.then(
(commandResult) => VSCode.window.showInformationMessage(""+commandResult),
(e) => {
VSCode.window.showErrorMessage("Error" + e);
}
);
}, 10000);
return commons.activate(options, context);
}