WIP: exploring multi-root workspace protocol in vscode + boot java ls

Note: this code is not working, probably better to start over when the
time comes. This branch is saved just in case it helps seeing the kinds
of changes we might want/need to do.
This commit is contained in:
Kris De Volder
2017-11-08 09:52:44 -08:00
parent 569b4df211
commit 0cf1e64608
18 changed files with 589 additions and 48 deletions

View File

@@ -10,8 +10,12 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;
import java.io.File;
import java.net.URI;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionItemKind;
@@ -55,12 +59,14 @@ 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.WorkspaceFolder;
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;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
@@ -173,7 +179,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
CompletableFuture<InitializeResult> result = super.initialize(params);
this.indexer.initialize(this.getWorkspaceRoot());
this.indexer.initialize(getWorkspaceRoots());
return result;
}
@@ -360,4 +366,5 @@ public class BootJavaLanguageServer extends SimpleLanguageServer {
public CompilationUnitCache getCompilationUnitCache() {
return cuCache;
}
}

View File

@@ -53,6 +53,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.text.LanguageId;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -102,14 +103,14 @@ public class SpringIndexer {
this.symbols = Collections.synchronizedList(new ArrayList<>());
this.symbolsByDoc = new ConcurrentHashMap<>();
}
public CompletableFuture<Void> initialize(final Path workspaceRoot) {
if (workspaceRoot==null) {
return CompletableFuture.completedFuture(null);
}
public CompletableFuture<Void> initialize(Collection<WorkspaceFolder> workspaceRoots) {
synchronized(this) {
server.getWorkspaceService().onDidChangeWorkspaceFolders(evt -> {
System.err.println("Workspace roots have changed!");
refresh();
});
if (this.initializeTask == null) {
initializing.set(true);
if (server.getProjectObserver() != null) {
@@ -118,7 +119,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() {
@@ -139,13 +142,12 @@ public class SpringIndexer {
}
}
}, "Spring Annotation Index Update Worker");
updateWorker.start();
initializing.set(false);
}
});
}
return this.initializeTask;
}
}
@@ -161,7 +163,7 @@ public class SpringIndexer {
symbols.clear();
symbolsByDoc.clear();
Log.info("Rebuilding SpringIndexer...");
initialize(server.getWorkspaceRoot());
initialize(server.getWorkspaceRoots());
}
}
@@ -264,9 +266,9 @@ public class SpringIndexer {
return queryindex == queryChars.length;
}
private void scanFiles(File directory) {
private void scanFiles(WorkspaceFolder directory) {
try {
Map<Optional<IJavaProject>, List<String>> projects = Files.walk(directory.toPath())
Map<Optional<IJavaProject>, List<String>> projects = Files.walk(Paths.get(new URI(directory.getUri())))
.filter(path -> path.getFileName().toString().endsWith(".java"))
.filter(Files::isRegularFile)
.map(path -> path.toAbsolutePath().toString())
@@ -280,6 +282,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);

View File

@@ -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<List<? extends Location>> findReferencesFromPropertyFiles(Path workspaceRoot,
String propertyKey) {
public CompletableFuture<List<? extends Location>> findReferencesFromPropertyFiles(
Collection<WorkspaceFolder> workspaceRoots,
String propertyKey
) {
for (WorkspaceFolder workspaceFolder : workspaceRoots) {
try {
Path workspaceRoot = Paths.get(new URI(workspaceFolder.getUri()));
try (Stream<Path> walk = Files.walk(workspaceRoot)) {
List<Location> locations = walk
.filter(path -> isPropertiesFile(path))
.filter(path -> path.toFile().isFile())
.map(path -> findReferences(path, propertyKey))
.flatMap(Collection::stream)
.collect(Collectors.toList());
try (Stream<Path> walk = Files.walk(workspaceRoot)) {
List<Location> 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;

View File

@@ -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;
@@ -28,9 +29,12 @@ import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider;
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
*/
@@ -55,7 +59,7 @@ public class SpringIndexerBeansTest {
public void testScanSimpleConfigurationClass() throws Exception {
SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders);
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
indexer.initialize(directory.toPath());
indexer.initialize(wsFolder(directory));
String uriPrefix = "file://" + directory.getAbsolutePath();
List<? extends SymbolInformation> symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/SimpleConfiguration.java");
@@ -63,11 +67,21 @@ public class SpringIndexerBeansTest {
assertTrue(containsSymbol(symbols, "@+ 'simpleBean' (@Bean) BeanClass", uriPrefix + "/src/main/java/org/test/SimpleConfiguration.java", 8, 1, 8, 8));
}
private Collection<WorkspaceFolder> wsFolder(File directory) {
if (directory!=null) {
return ImmutableList.of(new WorkspaceFolder(
directory.toURI().toString(),
directory.getName()
));
}
return ImmutableList.of();
}
@Test
public void testScanSimpleFunctionBean() throws Exception {
SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders);
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
indexer.initialize(directory.toPath());
indexer.initialize(wsFolder(directory));
String uriPrefix = "file://" + directory.getAbsolutePath();
List<? extends SymbolInformation> symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/FunctionClass.java");
@@ -79,7 +93,7 @@ public class SpringIndexerBeansTest {
public void testScanSimpleComponentClass() throws Exception {
SpringIndexer indexer = new SpringIndexer(harness.getServer(), projectFinder, symbolProviders);
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
indexer.initialize(directory.toPath());
indexer.initialize(wsFolder(directory));
String uriPrefix = "file://" + directory.getAbsolutePath();
List<? extends SymbolInformation> symbols = indexer.getSymbols(uriPrefix + "/src/main/java/org/test/SimpleComponent.java");

View File

@@ -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<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(root, "test.property");
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
@@ -49,12 +54,22 @@ public class PropertyReferenceFinderTest {
assertEquals(13, location.getRange().getEnd().getCharacter());
}
private Collection<WorkspaceFolder> 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<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(root, "test.property");
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
assertNotNull(resultFuture);
List<? extends Location> 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<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(root, "server.port");
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "server.port");
assertNotNull(resultFuture);
List<? extends Location> 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<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(root, "appl1.prop");
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
assertNotNull(resultFuture);
List<? extends Location> 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<? extends Location> 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<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(root, "appl1.prop");
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();