From 285e4fe8117999c850201644621afb964c2f18dd Mon Sep 17 00:00:00 2001 From: nsingh Date: Wed, 14 Mar 2018 14:42:35 -0700 Subject: [PATCH] Initial implementation for adding classpath listener --- .../languageserver/STS4LanguageClient.java | 5 + .../jdt/ls/ClasspathListener.java | 18 +++ .../jdt/ls/ClasspathListenerManager.java | 58 +++++++++ .../jdt/ls/ClasspathListenerParams.java | 35 ++++++ .../jdt/ls/ClasspathListenerResponse.java | 15 +++ .../util/SimpleLanguageServer.java | 26 +++++ .../testharness/LanguageServerHarness.java | 8 ++ .../extension/ClasspathListenerManager.java | 110 ++++++++++++++++++ .../ls/extension/ResolveClasspathHandler.java | 7 -- .../vscode/boot/jdt/ls/JdtLsProjectCache.java | 9 ++ 10 files changed, 284 insertions(+), 7 deletions(-) create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListener.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerParams.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerResponse.java create mode 100644 headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerManager.java diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java index d5f16bdd9..8aad7df17 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/STS4LanguageClient.java @@ -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 project(String uri); + + @JsonRequest("sts/addClasspathListener") + CompletableFuture addClasspathListener(ClasspathListenerParams params); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListener.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListener.java new file mode 100644 index 000000000..6016680e3 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListener.java @@ -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); + + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java new file mode 100644 index 000000000..8bf460e16 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerManager.java @@ -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) + ))); + }; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerParams.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerParams.java new file mode 100644 index 000000000..29db176ae --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerParams.java @@ -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 + "]"; + } + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerResponse.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerResponse.java new file mode 100644 index 000000000..aea750923 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/ClasspathListenerResponse.java @@ -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 { + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 71a4ec0c7..910b0e2d9 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -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 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); + } } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 374dcb9f5..20bae53c8 100644 --- a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -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 { return CompletableFuture.completedFuture(null); } + @Override + public CompletableFuture addClasspathListener( + ClasspathListenerParams params) { + return CompletableFuture.completedFuture(null); + } + }); } diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerManager.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerManager.java new file mode 100644 index 000000000..5574b5220 --- /dev/null +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ClasspathListenerManager.java @@ -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; + } + } + +} \ No newline at end of file diff --git a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java index fa3ad9271..7e0cabcb8 100644 --- a/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java +++ b/headless-services/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/ResolveClasspathHandler.java @@ -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 diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index e9123555b..d04578b8b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -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