Partial implementation of 'jdt.ls.addClasspatListener' support
This commit is contained in:
@@ -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)
|
||||
)));
|
||||
)));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user