diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index 5436ee728..acc1a9e2b 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -17,6 +18,8 @@ import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.Registration; +import org.eclipse.lsp4j.RegistrationParams; import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareFactoryManager; import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider; import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider; @@ -56,6 +59,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; 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.multiroot.WorkspaceFoldersProposedService; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler; @@ -174,13 +178,17 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { public CompletableFuture initialize(InitializeParams params) { CompletableFuture result = super.initialize(params); - this.indexer.initialize(this.getWorkspaceRoot()); + this.indexer.initialize(getWorkspaceRoots()); return result; } @Override public void initialized() { + Registration registration = new Registration(WorkspaceFoldersProposedService.CAPABILITY_ID, WorkspaceFoldersProposedService.CAPABILITY_NAME, null); + RegistrationParams registrationParams = new RegistrationParams(Collections.singletonList(registration)); + getClient().registerCapability(registrationParams); + // TODO: due to a missing message from lsp4e this "initialized" is not called in // the LSP4E case // if this gets fixed, the code should move here (from "initialize" above) @@ -360,4 +368,5 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { public CompilationUnitCache getCompilationUnitCache() { return cuCache; } + } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index f164d1e45..15b79c0f4 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -54,6 +54,7 @@ import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.util.text.LanguageId; @@ -105,12 +106,13 @@ public class SpringIndexer { this.symbols = Collections.synchronizedList(new ArrayList<>()); this.symbolsByDoc = new ConcurrentHashMap<>(); + server.getWorkspaceService().onDidChangeWorkspaceFolders(evt -> { + System.err.println("Workspace roots have changed!"); + refresh(); + }); } - public CompletableFuture initialize(final Path workspaceRoot) { - if (workspaceRoot==null) { - return CompletableFuture.completedFuture(null); - } + public CompletableFuture initialize(Collection workspaceRoots) { synchronized(this) { if (this.initializeTask == null) { initializing.set(true); @@ -120,7 +122,9 @@ public class SpringIndexer { this.initializeTask = CompletableFuture.runAsync(new Runnable() { @Override public void run() { - scanFiles(workspaceRoot.toFile()); + for (WorkspaceFolder root : workspaceRoots) { + scanFiles(root); + } SpringIndexer.this.updateQueue = new LinkedBlockingQueue<>(); SpringIndexer.this.updateWorker = new Thread(new Runnable() { @@ -141,13 +145,12 @@ public class SpringIndexer { } } }, "Spring Annotation Index Update Worker"); - updateWorker.start(); initializing.set(false); } + }); } - return this.initializeTask; } } @@ -163,7 +166,7 @@ public class SpringIndexer { symbols.clear(); symbolsByDoc.clear(); Log.info("Rebuilding SpringIndexer..."); - initialize(server.getWorkspaceRoot()); + initialize(server.getWorkspaceRoots()); } } @@ -252,9 +255,9 @@ public class SpringIndexer { return null; } - private void scanFiles(File directory) { + private void scanFiles(WorkspaceFolder directory) { try { - Map, List> projects = Files.walk(directory.toPath()) + Map, List> projects = Files.walk(Paths.get(new URI(directory.getUri()))) .filter(path -> path.getFileName().toString().endsWith(".java")) .filter(Files::isRegularFile) .map(path -> path.toAbsolutePath().toString()) @@ -268,6 +271,7 @@ public class SpringIndexer { } private void scanProject(IJavaProject project, String[] files) { + System.err.println("scan project: "+project); try { ASTParser parser = ASTParser.newParser(AST.JLS8); String[] classpathEntries = getClasspathEntries(project); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java index 8b015cad9..20bbae458 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValuePropertyReferencesProvider.java @@ -34,6 +34,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -94,7 +95,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { if (range != null) { String propertyKey = value.substring(range.getStart(), range.getEnd()); if (propertyKey != null && propertyKey.length() > 0) { - return findReferencesFromPropertyFiles(languageServer.getWorkspaceRoot(), propertyKey); + return findReferencesFromPropertyFiles(languageServer.getWorkspaceRoots(), propertyKey); } } } @@ -105,21 +106,26 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider { return null; } - public CompletableFuture> findReferencesFromPropertyFiles(Path workspaceRoot, - String propertyKey) { + public CompletableFuture> findReferencesFromPropertyFiles( + Collection workspaceRoots, + String propertyKey + ) { + for (WorkspaceFolder workspaceFolder : workspaceRoots) { + try { + Path workspaceRoot = Paths.get(new URI(workspaceFolder.getUri())); + try (Stream walk = Files.walk(workspaceRoot)) { + List locations = walk + .filter(path -> isPropertiesFile(path)) + .filter(path -> path.toFile().isFile()) + .map(path -> findReferences(path, propertyKey)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); - try (Stream walk = Files.walk(workspaceRoot)) { - List locations = walk - .filter(path -> isPropertiesFile(path)) - .filter(path -> path.toFile().isFile()) - .map(path -> findReferences(path, propertyKey)) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - - return CompletableFuture.completedFuture(locations); - } - catch (Exception e) { - e.printStackTrace(); + return CompletableFuture.completedFuture(locations); + } + } catch (Exception e) { + e.printStackTrace(); + } } return null; diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java index 4a4c14944..48a50628e 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerBeansTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -30,9 +31,12 @@ import org.springframework.ide.vscode.boot.java.beans.test.SpringIndexerHarness. import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import com.google.common.collect.ImmutableList; + /** * @author Martin Lippert */ @@ -57,7 +61,7 @@ public class SpringIndexerBeansTest { public void testScanSimpleConfigurationClass() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); indexer.assertDocumentSymbols(uriPrefix + "/src/main/java/org/test/SimpleConfiguration.java", @@ -71,7 +75,7 @@ public class SpringIndexerBeansTest { @Test public void testScanSpecialConfigurationClass() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); String docUri = uriPrefix + "/src/main/java/org/test/SpecialConfiguration.java"; @@ -103,7 +107,7 @@ public class SpringIndexerBeansTest { public void testScanSimpleFunctionBean() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); indexer.assertDocumentSymbols(uriPrefix + "/src/main/java/org/test/FunctionClass.java", @@ -119,7 +123,7 @@ public class SpringIndexerBeansTest { public void testScanSimpleComponentClass() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); indexer.assertDocumentSymbols(uriPrefix + "/src/main/java/org/test/SimpleComponent.java", @@ -134,7 +138,7 @@ public class SpringIndexerBeansTest { @Test public void testScanSimpleControllerClass() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); String docUri = uriPrefix + "/src/main/java/org/test/SimpleController.java"; @@ -148,7 +152,7 @@ public class SpringIndexerBeansTest { @Test public void testScanRestControllerClass() throws Exception { SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServer(), projectFinder, symbolProviders); File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()); - indexer.initialize(directory.toPath()); + indexer.initialize(indexer.wsFolder(directory)); String uriPrefix = "file://" + directory.getAbsolutePath(); String docUri = uriPrefix + "/src/main/java/org/test/SimpleRestController.java"; diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerHarness.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerHarness.java index 8064b7252..141e3afdd 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerHarness.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/SpringIndexerHarness.java @@ -12,14 +12,14 @@ package org.springframework.ide.vscode.boot.java.beans.test; import static org.junit.Assert.assertEquals; +import java.io.File; import java.net.URI; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Map; import org.apache.commons.io.IOUtils; import org.eclipse.lsp4j.Range; @@ -29,6 +29,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyA import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.boot.java.utils.SpringIndexer; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.languageserver.testharness.Editor; @@ -139,7 +140,17 @@ public class SpringIndexerHarness { return ImmutableList.of(); } - public void initialize(Path wsRoot) { - indexer.initialize(wsRoot); + public Collection wsFolder(File directory) { + if (directory!=null) { + return ImmutableList.of(new WorkspaceFolder( + directory.toURI().toString(), + directory.getName() + )); + } + return ImmutableList.of(); + } + + public void initialize(Collection wsRoots) { + indexer.initialize(wsRoots); } } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java index 3ffec6073..4ac7509e2 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/references/test/PropertyReferenceFinderTest.java @@ -13,17 +13,22 @@ package org.springframework.ide.vscode.boot.java.references.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.io.File; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.Location; import org.junit.Test; import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import com.google.common.collect.ImmutableList; + /** * @author Martin Lippert */ @@ -34,7 +39,7 @@ public class PropertyReferenceFinderTest { ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null); Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-case/").toURI()); - CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(root, "test.property"); + CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property"); assertNotNull(resultFuture); List locations = resultFuture.get(); @@ -49,12 +54,22 @@ public class PropertyReferenceFinderTest { assertEquals(13, location.getRange().getEnd().getCharacter()); } + private Collection wsFolder(Path directory) { + if (directory!=null) { + return ImmutableList.of(new WorkspaceFolder( + directory.toUri().toString(), + directory.getFileName().toString() + )); + } + return ImmutableList.of(); + } + @Test public void testFindReferenceAtBeginningYMLFile() throws Exception { ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null); Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-yml/").toURI()); - CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(root, "test.property"); + CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property"); assertNotNull(resultFuture); List locations = resultFuture.get(); @@ -74,7 +89,7 @@ public class PropertyReferenceFinderTest { ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null); Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-case/").toURI()); - CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(root, "server.port"); + CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "server.port"); assertNotNull(resultFuture); List locations = resultFuture.get(); @@ -94,7 +109,7 @@ public class PropertyReferenceFinderTest { ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null); Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/multiple-files/").toURI()); - CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(root, "appl1.prop"); + CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop"); assertNotNull(resultFuture); List locations = resultFuture.get(); @@ -121,14 +136,14 @@ public class PropertyReferenceFinderTest { assertEquals(1, location.getRange().getEnd().getLine()); assertEquals(10, location.getRange().getEnd().getCharacter()); } - + private Location getLocation(List locations, URI docURI) { for (Location location : locations) { if (docURI.toString().equals(location.getUri())) { return location; } } - + return null; } @@ -137,7 +152,7 @@ public class PropertyReferenceFinderTest { ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null); Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/mixed-multiple-files/").toURI()); - CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(root, "appl1.prop"); + CompletableFuture> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop"); assertNotNull(resultFuture); List locations = resultFuture.get(); diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java index a8711f3b7..759d77d70 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/LaunguageServerApp.java @@ -177,7 +177,7 @@ public abstract class LaunguageServerApp { * @return */ protected ExecutorService createServerThreads() { - return Executors.newSingleThreadExecutor(); + return Executors.newCachedThreadPool(); } private Launcher createSocketLauncher(Object localService, Class remoteInterface, SocketAddress socketAddress, ExecutorService executorService, Function wrapper) throws IOException { 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 c8f868ae2..969a32a01 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 @@ -23,7 +23,7 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixEd * * @author Kris De Volder */ -public interface STS4LanguageClient extends LanguageClient { +public interface STS4LanguageClient extends LanguageClient, WorkspaceFoldersProposedClient { @JsonNotification("sts/highlight") void highlight(HighlightParams highlights); diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/WorkspaceFoldersProposedClient.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/WorkspaceFoldersProposedClient.java new file mode 100644 index 000000000..d5127e39e --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/WorkspaceFoldersProposedClient.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * 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: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; + +public interface WorkspaceFoldersProposedClient { + + /** + * The workspace/workspaceFolders request is sent from the server to the client + * to fetch the current open list of workspace folders. + * + * @return Returns the current open list of workspace folders. Returns null in + * the response if only a single file is open in the tool. Returns an + * empty array if a workspace is open but no folders are configured. + */ + @JsonRequest("workspace/workspaceFolders") + CompletableFuture getWorkspaceFolders(); + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/DidChangeWorkspaceFoldersParams.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/DidChangeWorkspaceFoldersParams.java new file mode 100644 index 000000000..49cb1f521 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/DidChangeWorkspaceFoldersParams.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * 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: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.multiroot; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; + +public class DidChangeWorkspaceFoldersParams { + /** + * The actual workspace folder change event. + */ + @NonNull + private WorkspaceFoldersChangeEvent event; + + public DidChangeWorkspaceFoldersParams() { + // TODO Auto-generated constructor stub + } + + /** + * @param event + */ + public DidChangeWorkspaceFoldersParams(@NonNull WorkspaceFoldersChangeEvent event) { + this.event = event; + } + + /** + * @return the event + */ + @NonNull + public WorkspaceFoldersChangeEvent getEvent() { + return event; + } + + /** + * @param event + * the event to set + */ + public void setEvent(@NonNull WorkspaceFoldersChangeEvent event) { + this.event = event; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "DidChangeWorkspaceFoldersParams [event=" + event + "]"; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((event == null) ? 0 : event.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DidChangeWorkspaceFoldersParams other = (DidChangeWorkspaceFoldersParams) obj; + if (event == null) { + if (other.event != null) { + return false; + } + } else if (!event.equals(other.event)) { + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/README.txt b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/README.txt new file mode 100644 index 000000000..44172b93a --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/README.txt @@ -0,0 +1,4 @@ +Source code in this folder are copied from org.eclipse.jdt.ls.core.internal.lsp + +They provide support for the proposed LSP protocol extension to deal with multi-root +workspaces. \ No newline at end of file diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFolder.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFolder.java new file mode 100644 index 000000000..9bc15d72f --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFolder.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * 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: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.multiroot; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; + +public class WorkspaceFolder { + /** + * The associated URI for this workspace folder. + */ + @NonNull + private String uri; + + /** + * The name of the workspace folder. Defaults to the uri's basename. + */ + @NonNull + private String name; + + public WorkspaceFolder() { + } + + public WorkspaceFolder(@NonNull String uri, @NonNull String name) { + this.uri = uri; + this.name = name; + } + + /** + * Gets the associated URI for this workspace folder. + * + * @return the uri + */ + @NonNull + public String getUri() { + return uri; + } + + /** + * Sets the associated URI for this workspace folder. + * + * @param uri + * the uri to set + */ + public void setUri(@NonNull String uri) { + this.uri = uri; + } + + /** + * The name of the workspace folder. Defaults to the uri's basename. + * + * @return the name + */ + @NonNull + public String getName() { + return name; + } + + /** + * The name of the workspace folder. Defaults to the uri's basename. + * + * @param name + * the name to set + */ + public void setName(@NonNull String name) { + this.name = name; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "WorkspaceFolder [uri=" + uri + ", name=" + name + "]"; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((uri == null) ? 0 : uri.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + WorkspaceFolder other = (WorkspaceFolder) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (uri == null) { + if (other.uri != null) { + return false; + } + } else if (!uri.equals(other.uri)) { + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersChangeEvent.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersChangeEvent.java new file mode 100644 index 000000000..6f96fb2d8 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersChangeEvent.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * 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: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.multiroot; + +import java.util.Arrays; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; + +/** + * The workspace folder change event. + */ +public class WorkspaceFoldersChangeEvent { + /** + * The array of added workspace folders + */ + @NonNull + private WorkspaceFolder[] added; + + /** + * The array of the removed workspace folders + */ + @NonNull + private WorkspaceFolder[] removed; + + public WorkspaceFoldersChangeEvent() { + } + + public WorkspaceFoldersChangeEvent(@NonNull final WorkspaceFolder[] added, @NonNull final WorkspaceFolder[] removed) { + this.added = added; + this.removed = removed; + } + + /** + * @return the added + */ + @NonNull + public WorkspaceFolder[] getAdded() { + return added; + } + + /** + * @param added + * the added to set + */ + public void setAdded(@NonNull WorkspaceFolder[] added) { + this.added = added; + } + + /** + * @return the removed + */ + @NonNull + public WorkspaceFolder[] getRemoved() { + return removed; + } + + /** + * @param removed + * the removed to set + */ + public void setRemoved(@NonNull WorkspaceFolder[] removed) { + this.removed = removed; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(added); + result = prime * result + Arrays.hashCode(removed); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + WorkspaceFoldersChangeEvent other = (WorkspaceFoldersChangeEvent) obj; + if (!Arrays.equals(added, other.added)) { + return false; + } + if (!Arrays.equals(removed, other.removed)) { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "WorkspaceFoldersChangeEvent [added=" + Arrays.toString(added) + ", removed=" + Arrays.toString(removed) + "]"; + } + +} \ No newline at end of file diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersProposedService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersProposedService.java new file mode 100644 index 000000000..0ce838650 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/multiroot/WorkspaceFoldersProposedService.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2017 Microsoft Corporation and others. + * 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: + * Microsoft Corporation - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.commons.languageserver.multiroot; + +import java.util.UUID; + +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +/** + * Server APIs for Workspace folders (prop + * https://github.com/Microsoft/vscode-languageserver-node/blob/master/protocol/src/protocol.workspaceFolders.proposed.md + * + */ +@JsonSegment("workspace") +public interface WorkspaceFoldersProposedService { + + public static final String CAPABILITY_NAME = "workspace/didChangeWorkspaceFolders"; + public static final String CAPABILITY_ID = UUID.randomUUID().toString(); + + /** + * The workspace/didChangeWorkspaceFolders notification is sent from the client + * to the server to inform the client about workspace folder configuration + * changes. + * + * @param documentUri + * the document from which the project configuration will be updated + */ + @JsonNotification + void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params); + + + +} + 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 328134aff..6c097efd9 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 @@ -13,8 +13,10 @@ package org.springframework.ide.vscode.commons.languageserver.util; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -43,6 +45,9 @@ import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; 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.multiroot.DidChangeWorkspaceFoldersParams; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFoldersProposedService; 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; @@ -70,7 +75,7 @@ import reactor.core.scheduler.Schedulers; * here so we can try to keep the subclass itself more 'clutter free' and focus on * what its really doing and not the 'wiring and plumbing'. */ -public abstract class SimpleLanguageServer implements LanguageServer, LanguageClientAware, ServiceNotificationsClient { +public abstract class SimpleLanguageServer implements LanguageServer, LanguageClientAware, ServiceNotificationsClient, WorkspaceFoldersProposedService { private static final Scheduler RECONCILER_SCHEDULER = Schedulers.newSingle("Reconciler"); @@ -78,8 +83,6 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl private final String CODE_ACTION_COMMAND_ID; protected final LazyCompletionResolver completionResolver = createCompletionResolver(); - private Path workspaceRoot; - private SimpleTextDocumentService tds; private SimpleWorkspaceService workspace; @@ -169,16 +172,28 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl @Override public CompletableFuture initialize(InitializeParams params) { Log.debug("Initializing: "+params); - String rootPath = params.getRootPath(); - if (rootPath==null) { - Log.debug("workspaceRoot NOT SET"); - } else { - this.workspaceRoot= Paths.get(rootPath).toAbsolutePath().normalize(); + + // multi-root workspace handling + List workspaceFolders = getWorkspaceFolders(params); + if (!workspaceFolders.isEmpty()) { + this.getWorkspaceService().setWorkspaceFolders(workspaceFolders); } + else { + String rootPath = params.getRootPath(); + if (rootPath==null) { + Log.debug("workspaceRoot NOT SET"); + } else { + List singleRootFolder = new ArrayList<>(); + Path path = Paths.get(rootPath); + singleRootFolder.add(new WorkspaceFolder(path.toUri().toString(), path.getFileName().toString())); + this.getWorkspaceService().setWorkspaceFolders(singleRootFolder); + } + } + this.hasCompletionSnippetSupport = safeGet(false, () -> params.getCapabilities().getTextDocument().getCompletion().getCompletionItem().getSnippetSupport()); this.hasExecuteCommandSupport = safeGet(false, () -> params.getCapabilities().getWorkspace().getExecuteCommand()!=null); this.hasFileWatcherRegistrationSupport = safeGet(false, () -> params.getCapabilities().getWorkspace().getDidChangeWatchedFiles().getDynamicRegistration()); - Log.debug("workspaceRoot = "+workspaceRoot); + Log.debug("workspaceRoots = "+getWorkspaceService().getWorkspaceRoots()); Log.debug("hasCompletionSnippetSupport = "+hasCompletionSnippetSupport); Log.debug("hasExecuteCommandSupport = "+hasExecuteCommandSupport); @@ -193,6 +208,31 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl return CompletableFuture.completedFuture(result); } + private List getWorkspaceFolders(InitializeParams params) { + List initialFolders = new ArrayList<>(); + + Object initOptions = params.getInitializationOptions(); + if (initOptions != null && initOptions instanceof Map) { + Map initializationOptions = (Map) initOptions; + Object folders = initializationOptions.get("workspaceFolders"); + if (folders != null && folders instanceof List) { + List workspaceFolders = (List) folders; + for (Object object : workspaceFolders) { + String folderPath = object.toString(); + String folderName = null; + + int folderNameStart = folderPath.lastIndexOf("/"); + if (folderNameStart > 0) { + folderName = folderPath.substring(folderPath.lastIndexOf("/") + 1); + } + initialFolders.add(new WorkspaceFolder(object.toString(), folderName)); + } + } + } + + return initialFolders; + } + /** * Get some info safely. If there's any kind of exception, ignore it * and retutn default value instead. @@ -307,10 +347,29 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl System.exit(0); } - public Path getWorkspaceRoot() { - return workspaceRoot; + public Collection getWorkspaceRoots() { + return getWorkspaceService().getWorkspaceRoots(); } +// /** +// * Deprecated, shouldn't use and should be removed. Anyone calling this +// * will have problems handling multi-root workspaces. +// *

+// * Use getWorkspaceRoots instead. +// */ +// @Deprecated +// public Path getWorkspaceRoot() { +// try { +// Optional firstRoot = getWorkspaceRoots().stream().findFirst(); +// if (firstRoot.isPresent()) { +// return new File(new URI(firstRoot.get().getUri())).toPath(); +// } +// } catch (Exception e) { +// Log.log(e); +// } +// return null; +// } + @Override public synchronized SimpleTextDocumentService getTextDocumentService() { if (tds==null) { @@ -467,4 +526,10 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl public boolean canRegisterFileWatchersDynamically() { return hasFileWatcherRegistrationSupport; } + + @Override + public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) { + getWorkspaceService().didChangeWorkspaceFolders(params); + } + } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java index 1a8097e8f..2463db6e1 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleWorkspaceService.java @@ -10,7 +10,10 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.util; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -20,6 +23,10 @@ import org.eclipse.lsp4j.ExecuteCommandParams; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.WorkspaceSymbolParams; import org.eclipse.lsp4j.services.WorkspaceService; +import org.springframework.ide.vscode.commons.languageserver.multiroot.DidChangeWorkspaceFoldersParams; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFoldersChangeEvent; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFoldersProposedService; import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.Log; @@ -28,15 +35,18 @@ import com.google.common.collect.ImmutableList; import reactor.core.publisher.Mono; -public class SimpleWorkspaceService implements WorkspaceService { +public class SimpleWorkspaceService implements WorkspaceService, WorkspaceFoldersProposedService { private SimpleLanguageServer server; + private Set workspaceRoots = new HashSet<>(); private ListenerList configurationListeners = new ListenerList<>(); private ExecuteCommandHandler executeCommandHandler; private WorkspaceSymbolHandler workspaceSymbolHandler; private SimpleServerFileObserver fileObserver; + private ListenerList workspaceFolderListeners = new ListenerList<>(); + public SimpleWorkspaceService(SimpleLanguageServer server) { this.server = server; this.fileObserver = new SimpleServerFileObserver(server); @@ -89,6 +99,27 @@ public class SimpleWorkspaceService implements WorkspaceService { }); } + @Override + public synchronized void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) { + WorkspaceFoldersChangeEvent evt = params.getEvent(); + boolean changed = false; + for (WorkspaceFolder r : evt.getAdded()) { + workspaceRoots.add(r); + changed = true; + } + for (WorkspaceFolder r : evt.getRemoved()) { + workspaceRoots.remove(r); + changed = true; + } + if (changed) { + workspaceFolderListeners.fire(params); + } + } + + public void onDidChangeWorkspaceFolders(Consumer l) { + workspaceFolderListeners.add(l); + } + @Override public CompletableFuture executeCommand(ExecuteCommandParams params) { if (this.executeCommandHandler!=null) { @@ -123,4 +154,14 @@ public class SimpleWorkspaceService implements WorkspaceService { fileObserver.dispose(); } + public Collection getWorkspaceRoots() { + return ImmutableList.copyOf(workspaceRoots); + } + + public synchronized void setWorkspaceFolders(List workspaceFolders) { + workspaceRoots = new HashSet<>(); + workspaceRoots.addAll(workspaceFolders); + } + + } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index e2ace4ecc..5c2c868f9 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -48,4 +48,9 @@ public class MavenJavaProject extends AbstractJavaProject { return classpath.update(); } + @Override + public String toString() { + return "MavenJavaProject("+classpath.getName()+")"; + } + } 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 0d13fb6be..afe6b9f07 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 @@ -94,6 +94,7 @@ import org.springframework.ide.vscode.commons.languageserver.HighlightParams; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; +import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder; 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; @@ -286,6 +287,16 @@ public class LanguageServerHarness { } return CompletableFuture.completedFuture(new ApplyWorkspaceEditResponse(false)); } + + @Override + public CompletableFuture getWorkspaceFolders() { + if (workspaceRoot!=null) { + return CompletableFuture.completedFuture(new WorkspaceFolder[]{ + new WorkspaceFolder(workspaceRoot.toURI().toString(), workspaceRoot.getName()) + }); + } + return CompletableFuture.completedFuture(new WorkspaceFolder[]{}); + } }); } diff --git a/vscode-extensions/commons-vscode/package.json b/vscode-extensions/commons-vscode/package.json index ce13187be..fec38eee4 100644 --- a/vscode-extensions/commons-vscode/package.json +++ b/vscode-extensions/commons-vscode/package.json @@ -30,6 +30,6 @@ "typescript": "^2.3.0", "@types/node": "^6.0.68", "vscode": "^1.1.5", - "vscode-languageclient": "^3.4.2" + "vscode-languageclient": "^3.4.5" } } diff --git a/vscode-extensions/commons-vscode/src/launch-util.ts b/vscode-extensions/commons-vscode/src/launch-util.ts index 39857c70c..6c04b35fc 100644 --- a/vscode-extensions/commons-vscode/src/launch-util.ts +++ b/vscode-extensions/commons-vscode/src/launch-util.ts @@ -16,6 +16,7 @@ import { Trace, NotificationType } from 'vscode-jsonrpc'; import * as P2C from 'vscode-languageclient/lib/protocolConverter'; import {WorkspaceEdit, Position} from 'vscode-languageserver-types'; import {HighlightService, HighlightParams} from './highlight-service'; +import { log } from 'util'; let p2c = P2C.createConverter(); @@ -83,7 +84,8 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon reader: socket, writer: socket }); - }).listen(port, () => { + }) + .listen(port, () => { let processLaunchoptions = { cwd: VSCode.workspace.rootPath }; @@ -151,6 +153,8 @@ function setupLanguageClient(context: VSCode.ExtensionContext, createServer: Ser let client = new LanguageClient(options.extensionId, options.extensionId, createServer, options.clientOptions ); + client.registerProposedFeatures(); + log("Proposed protocol extensions loaded!"); if (options.TRACE) { client.trace = Trace.Verbose; } diff --git a/vscode-extensions/vscode-boot-java/lib/Main.ts b/vscode-extensions/vscode-boot-java/lib/Main.ts index 78f0d4c5d..d94520247 100644 --- a/vscode-extensions/vscode-boot-java/lib/Main.ts +++ b/vscode-extensions/vscode-boot-java/lib/Main.ts @@ -10,7 +10,7 @@ import * as FS from 'fs'; import * as Net from 'net'; import * as ChildProcess from 'child_process'; import { LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo } from 'vscode-languageclient'; -import { TextDocument } from 'vscode'; +import { workspace, TextDocument } from 'vscode'; import { Trace } from 'vscode-jsonrpc'; import * as commons from 'commons-vscode'; @@ -38,7 +38,10 @@ export function activate(context: VSCode.ExtensionContext) { documentSelector: ['java'], synchronize: { configurationSection: 'boot-java' - } + }, + initializationOptions: { + workspaceFolders: workspace.workspaceFolders ? workspace.workspaceFolders.map(f => f.uri.toString()) : null + }, } }; commons.activate(options, context); diff --git a/vscode-extensions/vscode-boot-java/package.json b/vscode-extensions/vscode-boot-java/package.json index 4bb7d2cee..a50ce5f27 100644 --- a/vscode-extensions/vscode-boot-java/package.json +++ b/vscode-extensions/vscode-boot-java/package.json @@ -48,7 +48,7 @@ "vsce-package": "vsce package" }, "dependencies": { - "vscode-languageclient": "^3.4.2", + "vscode-languageclient": "^3.4.5", "commons-vscode": "^0.0.6" }, "devDependencies": {