Partial implementation of 'jdt.ls.addClasspatListener' support

This commit is contained in:
Kris De Volder
2018-03-15 08:56:18 -07:00
parent 285e4fe811
commit fcb4473025
6 changed files with 134 additions and 18 deletions

View File

@@ -10,12 +10,16 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.jdt.ls;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.Registration;
import org.eclipse.lsp4j.RegistrationParams;
import org.eclipse.lsp4j.Unregistration;
import org.eclipse.lsp4j.UnregistrationParams;
import org.springframework.ide.vscode.commons.languageserver.util.AsyncRunner;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import com.google.common.collect.ImmutableList;
@@ -28,30 +32,49 @@ public class ClasspathListenerManager {
private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand";
private int commandIdCounter = 0;
private SimpleLanguageServer server;
private AsyncRunner async;
public ClasspathListenerManager(SimpleLanguageServer server) {
this.server = server;
this.async = server.getAsync();
}
public Disposable addClasspathListener(ClasspathListener classpathListener) {
// TODO:
// 1. register callback command handler in SimpleLanguageServer
// 2. call the client to ask it to call that callback
// 3. register the callback command with the client
String callbackCommandId = "sts4.classpath." + (commandIdCounter++);
String registrationId = UUID.randomUUID().toString();
// 1. register callback command handler in SimpleLanguageServer
Disposable unregisterCommand = server.onCommand(callbackCommandId, (ExecuteCommandParams callbackParams) -> async.invoke(() -> {
List<Object> args = callbackParams.getArguments();
//Note: not sure... but args might be deserialized as com.google.gson.JsonElement's.
//If so the code below is not correct (casts will fail).
String projectUri = (String) args.get(0);
boolean deleted = args.size()>=2 && (Boolean)args.get(1);
classpathListener.changed(projectUri, deleted);
return "done";
}));
// 2. call the client to ask it to call that callback
CompletableFuture<ClasspathListenerResponse> future1 = server.getClient().addClasspathListener(new ClasspathListenerParams(callbackCommandId));
// 2. register the callback command with the client
String registrationId = UUID.randomUUID().toString();
RegistrationParams params = new RegistrationParams(ImmutableList.of(
new Registration(registrationId,
WORKSPACE_EXECUTE_COMMAND,
ImmutableMap.of("commands", ImmutableList.of(callbackCommandId))
)
));
server.getClient().registerCapability(params);
));
CompletableFuture<Void> future2 = server.getClient().registerCapability(params);
// Wait for async work
//future1.join();
//future2.join();
// Cleanups:
return () -> {
unregisterCommand.dispose();
this.server.getClient().unregisterCapability(new UnregistrationParams(ImmutableList.of(
new Unregistration(registrationId, WORKSPACE_EXECUTE_COMMAND)
)));
)));
};
}

View File

@@ -624,14 +624,28 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
this.initializeHandler = handler;
}
public void onInitialized(Runnable handler) {
Assert.isNull("Multiple initialized handlers not supported yet", this.initializedHandler);
this.initializedHandler = handler;
public synchronized void onInitialized(Runnable handler) {
if (this.initializedHandler==null) {
this.initializedHandler = handler;
} else {
Runnable oldHandler = this.initializedHandler;
this.initializedHandler = () -> {
oldHandler.run();
handler.run();
};
}
}
public void onShutdown(Runnable handler) {
Assert.isNull("Multiple shutdown handlers not supported yet", this.shutdownHandler);
this.shutdownHandler = handler;
public synchronized void onShutdown(Runnable handler) {
if (shutdownHandler==null) {
this.shutdownHandler = handler;
} else {
Runnable oldHandler = this.shutdownHandler;
this.shutdownHandler = () -> {
oldHandler.run();
handler.run();
};
}
}
public AsyncRunner getAsync() {

View File

@@ -11,5 +11,11 @@
<command id="sts.java.resolveProject"/>
</delegateCommandHandler>
</extension>
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
<delegateCommandHandler class="org.springframework.tooling.jdt.ls.extension.ClasspathListenerHandler">
<command id="sts.java.addClasspathListener"/>
<command id="sts.java.removeClasspathListener"/>
</delegateCommandHandler>
</extension>
</plugin>

View File

@@ -0,0 +1,56 @@
package org.springframework.tooling.jdt.ls.extension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
import org.eclipse.jdt.ls.core.internal.JavaClientConnection;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer;
import org.springframework.tooling.jdt.ls.extension.ClasspathListenerManager.ClasspathListener;
@SuppressWarnings("restriction")
public class ClasspathListenerHandler implements IDelegateCommandHandler {
static class MyClasspathListener implements ClasspathListener {
private ClasspathListenerManager manager = null;
private List<String> subscribers = new ArrayList<>(1);
public synchronized void subscribe(String callbackCommandId) {
if (manager==null) {
this.manager = new ClasspathListenerManager(this);
}
subscribers.add(callbackCommandId);
}
@Override
public void classpathChanged(IJavaProject jp) {
String project = jp.getProject().getLocationURI().toString();
boolean deleted = !jp.exists();
JavaClientConnection conn = JavaLanguageServerPlugin.getInstance().getClientConnection();
for (String callbackCommandId : subscribers) {
conn.executeCommand(callbackCommandId, Arrays.asList(project, deleted));
}
}
}
private static MyClasspathListener classpathListener = new MyClasspathListener();
@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
if (commandId.equals("sts.java.addClasspathListener")) {
return addClasspathListener((String)arguments.get(0));
}
return null;
}
private Object addClasspathListener(String callbackCommandId) {
classpathListener.subscribe(callbackCommandId);
return "ok";
}
}

View File

@@ -17,6 +17,7 @@ import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.slf4j.Logger;
@@ -40,6 +41,8 @@ import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import reactor.core.Disposable;
public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
private SimpleLanguageServer server;
@@ -48,14 +51,15 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
public JdtLsProjectCache(SimpleLanguageServer server) {
this.server = server;
this.server.addClasspathListener(new ClasspathListener() {
CompletableFuture<Disposable> disposable = new CompletableFuture<Disposable>();
this.server.onInitialized(() -> disposable.complete(server.addClasspathListener(new ClasspathListener() {
@Override
public void changed(String projectUri, boolean deleted) {
// TODO Auto-generated method stub
log.info("Classpath changed: "+projectUri);
}
});
})));
this.server.onShutdown(() -> disposable.thenAccept(Disposable::dispose));
}
@Override

View File

@@ -10,12 +10,25 @@ export function registerClasspathService(client : LanguageClient) : void {
client.onRequest(classpathRequest, async (params: ClasspathParams) => {
return await executeClasspathCommand(params.resourceUri);
});
let classpathListenerRequest = new RequestType<ClasspathListenerParams, ClasspathListenerResponse, void, void>("sts/addClasspathListener");
client.onRequest(classpathListenerRequest, async (params: ClasspathListenerParams) => {
return <ClasspathListenerResponse> await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.addClasspathListener", params.callbackCommandId);
});
}
async function executeClasspathCommand(resourceUri : string) : Promise<ClasspathResponse> {
return <ClasspathResponse> (await VSCode.commands.executeCommand("java.execute.workspaceCommand", "sts.java.resolveClasspath", resourceUri));
}
interface ClasspathListenerParams {
callbackCommandId: string
}
interface ClasspathListenerResponse {
}
interface ClasspathResponse {
entries: ClasspathEntry[],
defaultOutputFolder : string