Initial implementation for adding classpath listener

This commit is contained in:
nsingh
2018-03-14 14:42:35 -07:00
parent 9b249be598
commit 285e4fe811
10 changed files with 284 additions and 7 deletions

View File

@@ -16,6 +16,8 @@ import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.eclipse.lsp4j.services.LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListenerParams;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListenerResponse;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit.CursorMovement;
/**
@@ -39,4 +41,7 @@ public interface STS4LanguageClient extends LanguageClient {
@JsonRequest("sts/project")
CompletableFuture<ProjectResponse> project(String uri);
@JsonRequest("sts/addClasspathListener")
CompletableFuture<ClasspathListenerResponse> addClasspathListener(ClasspathListenerParams params);
}

View File

@@ -0,0 +1,18 @@
/*******************************************************************************
* 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.jdt.ls;
public interface ClasspathListener {
void changed(String projectUri, boolean deleted);
}

View File

@@ -0,0 +1,58 @@
/*******************************************************************************
* 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.jdt.ls;
import java.util.UUID;
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.SimpleLanguageServer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import reactor.core.Disposable;
public class ClasspathListenerManager {
private static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand";
private int commandIdCounter = 0;
private SimpleLanguageServer server;
public ClasspathListenerManager(SimpleLanguageServer server) {
this.server = server;
}
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();
RegistrationParams params = new RegistrationParams(ImmutableList.of(
new Registration(registrationId,
WORKSPACE_EXECUTE_COMMAND,
ImmutableMap.of("commands", ImmutableList.of(callbackCommandId))
)
));
server.getClient().registerCapability(params);
return () -> {
this.server.getClient().unregisterCapability(new UnregistrationParams(ImmutableList.of(
new Unregistration(registrationId, WORKSPACE_EXECUTE_COMMAND)
)));
};
}
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* 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.jdt.ls;
public class ClasspathListenerParams {
private String callbackCommandId;
public ClasspathListenerParams(String callbackCommandId) {
super();
this.callbackCommandId = callbackCommandId;
}
public String getCallbackCommandId() {
return callbackCommandId;
}
public void setCallbackCommandId(String callbackCommandId) {
this.callbackCommandId = callbackCommandId;
}
@Override
public String toString() {
return "ClasspathListenerParams [callbackCommandId=" + callbackCommandId + "]";
}
}

View File

@@ -0,0 +1,15 @@
/*******************************************************************************
* 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.jdt.ls;
public class ClasspathListenerResponse {
}

View File

@@ -15,6 +15,7 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -54,6 +55,8 @@ import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.LazyCompletionResolver;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListenerManager;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit;
@@ -74,6 +77,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import reactor.core.Disposable;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
@@ -128,7 +132,10 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
private Runnable shutdownHandler;
private Map<String, ExecuteCommandHandler> commands = new HashMap<>();
private AsyncRunner async = new AsyncRunner();
private ClasspathListenerManager classpathListenerManager;
@Override
public void connect(LanguageClient _client) {
@@ -297,6 +304,18 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
return deflt;
}
public Disposable onCommand(String id, ExecuteCommandHandler commandHandler) {
synchronized (commands) {
Assert.isLegal(!commands.containsKey(id));
commands.put(id, commandHandler);
}
return () -> {
synchronized (commands) {
commands.remove(id);
}
};
}
public void onError(String message, Throwable error) {
LanguageClient cl = this.client;
if (cl != null) {
@@ -618,4 +637,11 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
public AsyncRunner getAsync() {
return this.async;
}
public synchronized Disposable addClasspathListener(ClasspathListener classpathListener) {
if (classpathListenerManager == null) {
classpathListenerManager = new ClasspathListenerManager(this);
}
return classpathListenerManager.addClasspathListener(classpathListener);
}
}

View File

@@ -97,6 +97,8 @@ import org.springframework.ide.vscode.commons.languageserver.ProgressParams;
import org.springframework.ide.vscode.commons.languageserver.ProjectResponse;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListenerParams;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListenerResponse;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEdit.CursorMovement;
import org.springframework.ide.vscode.commons.languageserver.util.LanguageServerTestListener;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
@@ -305,6 +307,12 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<ClasspathListenerResponse> addClasspathListener(
ClasspathListenerParams params) {
return CompletableFuture.completedFuture(null);
}
});
}

View File

@@ -0,0 +1,110 @@
/*******************************************************************************
* 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 org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
/**
* An instance of this class provides a means to register
* listeners that get notified when classpath for a IJavaProject
* changes.
*
* @author Kris De Volder
*/
public class ClasspathListenerManager {
public interface ClasspathListener {
public abstract void classpathChanged(IJavaProject jp);
}
private class MyListener implements IElementChangedListener {
@Override
public void elementChanged(ElementChangedEvent event) {
visit(event.getDelta());
}
private void visit(IJavaElementDelta delta) {
IJavaElement el = delta.getElement();
switch (el.getElementType()) {
case IJavaElement.JAVA_MODEL:
visitChildren(delta);
break;
case IJavaElement.JAVA_PROJECT:
if (isClasspathChanged(delta.getFlags())) {
listener.classpathChanged((IJavaProject)el);
}
break;
default:
break;
}
}
private boolean isClasspathChanged(int flags) {
return 0!= (flags & (
IJavaElementDelta.F_CLASSPATH_CHANGED |
IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED
));
}
public void visitChildren(IJavaElementDelta delta) {
for (IJavaElementDelta c : delta.getAffectedChildren()) {
visit(c);
}
}
}
private ClasspathListener listener;
private MyListener myListener;
/**
* @param initialEvent If true, events are fired immediately on all existing java
* projects, treating the connection of the listener itself as a change event.
* This allows clients to become aware of all classpaths from the start and
* continually monitor them for changes from that point onward.
*/
public ClasspathListenerManager(ClasspathListener listener, boolean initialEvent) {
this.listener = listener;
if (initialEvent) {
for (IProject p : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
try {
if (p.isAccessible() && p.hasNature(JavaCore.NATURE_ID)) {
IJavaProject jp = JavaCore.create(p);
listener.classpathChanged(jp);
}
} catch (CoreException e) {
Logger.log(e);
}
}
}
JavaCore.addElementChangedListener(myListener=new MyListener(), ElementChangedEvent.POST_CHANGE);
}
public ClasspathListenerManager(ClasspathListener listener) {
this(listener, false);
}
public void dispose() {
if (myListener!=null) {
JavaCore.removeElementChangedListener(myListener);
myListener = null;
}
}
}

View File

@@ -31,13 +31,6 @@ public class ResolveClasspathHandler implements IDelegateCommandHandler {
public static final String ENTRY_KIND_SOURCE = "source";
public static final String ENTRY_KIND_BINARY = "binary";
public static final String OUTPUT_LOCATION = "output_location";
static {
Executors.newCachedThreadPool().execute(() -> {
JavaClientConnection client = JavaLanguageServerPlugin.getInstance().getClientConnection();
client.executeCommand("say.hello", "Hello from our redhat extension");
});
}
@Override

View File

@@ -32,6 +32,7 @@ import org.springframework.ide.vscode.commons.languageserver.ProjectResponse;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
@@ -47,6 +48,14 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
public JdtLsProjectCache(SimpleLanguageServer server) {
this.server = server;
this.server.addClasspathListener(new ClasspathListener() {
@Override
public void changed(String projectUri, boolean deleted) {
// TODO Auto-generated method stub
}
});
}
@Override