Wait for document symbols in sync fashion
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package org.springframework.ide.vscode.commons.languageserver.java;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener;
|
||||
|
||||
public class FutureProjectFinder implements DisposableBean {
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
private ProjectObserver projectObserver;
|
||||
|
||||
private WeakHashMap<URI, CompletableFuture<IJavaProject>> pendingFindProjectRequests = new WeakHashMap<>();
|
||||
|
||||
private final Listener LISTENER = new Listener() {
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
resolvePendingRequests(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public FutureProjectFinder(JavaProjectFinder projectFinder, Optional<ProjectObserver> projectObserver) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.projectObserver = projectObserver.orElse(null);
|
||||
if (this.projectObserver != null) {
|
||||
this.projectObserver.addListener(LISTENER);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private void resolvePendingRequests(IJavaProject project) {
|
||||
for (Map.Entry<URI, CompletableFuture<IJavaProject>> e : pendingFindProjectRequests.entrySet()) {
|
||||
Optional<IJavaProject> jp = projectFinder.find(new TextDocumentIdentifier(e.getKey().toString()));
|
||||
if (jp.isPresent()) {
|
||||
e.getValue().complete(jp.get());
|
||||
pendingFindProjectRequests.remove(e.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
if (projectObserver != null) {
|
||||
projectObserver.removeListener(LISTENER);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public CompletableFuture<IJavaProject> findFuture(URI uri) {
|
||||
TextDocumentIdentifier id = new TextDocumentIdentifier(uri.toString());
|
||||
Optional<IJavaProject> jp = projectFinder.find(id);
|
||||
if (jp.isPresent()) {
|
||||
return CompletableFuture.completedFuture(jp.get());
|
||||
} else {
|
||||
if (projectObserver == null) {
|
||||
throw new IllegalStateException("Future project lookup not supported without ProjectObserver bean present");
|
||||
}
|
||||
CompletableFuture<IJavaProject> cf = pendingFindProjectRequests.get(uri);
|
||||
if (cf == null) {
|
||||
cf = new CompletableFuture<IJavaProject>();
|
||||
pendingFindProjectRequests.put(uri, cf);
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
public static File findFile(File folder, String fileNameToFind, boolean recursiveUp) {
|
||||
if (folder != null && folder.exists()) {
|
||||
if (folder != null) {
|
||||
File file = new File(folder, fileNameToFind);
|
||||
if (file.isFile()) {
|
||||
return file;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
package org.springframework.ide.vscode.boot.app;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -54,7 +55,9 @@ import org.springframework.ide.vscode.boot.xml.SpringXMLCompletionEngine;
|
||||
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
|
||||
import org.springframework.ide.vscode.boot.yaml.completions.SpringYamlCompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LanguageServerRunner;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectFinder;
|
||||
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.util.DocumentEventListenerManager;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
@@ -225,4 +228,8 @@ public class BootLanguageServerBootApp {
|
||||
new SampleProjectsProvider()
|
||||
));
|
||||
}
|
||||
|
||||
@Bean FutureProjectFinder futureProjectFinder(JavaProjectFinder projectFinder, Optional<ProjectObserver> projectObserver) {
|
||||
return new FutureProjectFinder(projectFinder, projectObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -54,6 +55,7 @@ import org.springframework.ide.vscode.boot.java.utils.SymbolHandler;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.FutureProjectFinder;
|
||||
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.java.ProjectObserver.Listener;
|
||||
@@ -78,6 +80,7 @@ public class SpringSymbolIndex implements InitializingBean {
|
||||
@Autowired BootLanguageServerParams params;
|
||||
@Autowired AnnotationHierarchyAwareLookup<SymbolProvider> specificProviders;
|
||||
@Autowired SymbolCache cache;
|
||||
@Autowired FutureProjectFinder futureProjectFinder;
|
||||
|
||||
private static final String QUERY_PARAM_LOCATION_PREFIX = "locationPrefix:";
|
||||
private static final int MAX_NUMBER_OF_SYMBOLS_IN_RESPONSE = 50;
|
||||
@@ -120,7 +123,10 @@ public class SpringSymbolIndex implements InitializingBean {
|
||||
private String watchXMLDeleteRegistration;
|
||||
private String watchXMLCreatedRegistration;
|
||||
private String watchXMLChangedRegistration;
|
||||
|
||||
|
||||
// Futures resolved when project is initialized/indexed
|
||||
private Map<URI, CompletableFuture<Void>> initializedProjects = new HashMap<>();
|
||||
|
||||
|
||||
private SimpleWorkspaceService getWorkspaceService() {
|
||||
return server.getServer().getWorkspaceService();
|
||||
@@ -273,8 +279,16 @@ public class SpringSymbolIndex implements InitializingBean {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public CompletableFuture<Void> initializeProject(IJavaProject project) {
|
||||
CompletableFuture<Void> cf = _initializeProject(project);
|
||||
cf.thenAccept( f -> {
|
||||
projectInitializedFuture(project).complete(null);
|
||||
});
|
||||
return cf;
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> _initializeProject(IJavaProject project) {
|
||||
try {
|
||||
if (SpringProjectUtil.isBootProject(project) || SpringProjectUtil.isSpringProject(project)) {
|
||||
if (project.getElementName() == null) {
|
||||
@@ -291,7 +305,7 @@ public class SpringSymbolIndex implements InitializingBean {
|
||||
InitializeProject initializeItem = new InitializeProject(project, this.indexers[i]);
|
||||
futures[i] = CompletableFuture.runAsync(initializeItem, this.updateQueue);
|
||||
}
|
||||
|
||||
|
||||
return CompletableFuture.allOf(futures);
|
||||
}
|
||||
} else {
|
||||
@@ -520,21 +534,35 @@ public class SpringSymbolIndex implements InitializingBean {
|
||||
.filter(filter)
|
||||
.map(enhanced -> enhanced.getSymbol());
|
||||
}
|
||||
|
||||
public List<? extends SymbolInformation> getSymbols(String docURI) {
|
||||
List<EnhancedSymbolInformation> docSymbols = this.symbolsByDoc.get(docURI);
|
||||
if (docSymbols != null) {
|
||||
synchronized(docSymbols) {
|
||||
ImmutableList.Builder<SymbolInformation> builder = ImmutableList.builder();
|
||||
for (EnhancedSymbolInformation enhanced : docSymbols) {
|
||||
builder.add(enhanced.getSymbol());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
synchronized private CompletableFuture<Void> projectInitializedFuture(IJavaProject project) {
|
||||
if (project == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
URI uri = project.getLocationUri();
|
||||
return initializedProjects.computeIfAbsent(uri, u -> new CompletableFuture<Void>());
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
public List<? extends SymbolInformation> getSymbols(String docURI) {
|
||||
try {
|
||||
CompletableFuture<Void> projectInitialized = futureProjectFinder.findFuture(URI.create(docURI)).thenCompose(project -> projectInitializedFuture(project));
|
||||
projectInitialized.get(60, TimeUnit.SECONDS);
|
||||
List<EnhancedSymbolInformation> docSymbols = this.symbolsByDoc.get(docURI);
|
||||
if (docSymbols != null) {
|
||||
synchronized (docSymbols) {
|
||||
ImmutableList.Builder<SymbolInformation> builder = ImmutableList.builder();
|
||||
for (EnhancedSymbolInformation enhanced : docSymbols) {
|
||||
builder.add(enhanced.getSymbol());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<SymbolAddOnInformation> getAllAdditionalInformation(Predicate<SymbolAddOnInformation> filter) {
|
||||
|
||||
@@ -20,20 +20,30 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SourceLinksTestConf;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoConf;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SourceLinksTestConf.class)
|
||||
//@BootLanguageServerTest
|
||||
@OverrideAutoConfiguration(enabled=false)
|
||||
@Import({LanguageServerAutoConf.class, SourceLinksTestConf.class})
|
||||
@SpringBootTest(classes={
|
||||
BootLanguageServerBootApp.class
|
||||
})
|
||||
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class AdvancedSourceLinksTest {
|
||||
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
@@ -25,24 +25,34 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SymbolIndexConfig;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoConf;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(XmlBeansTestConf.class)
|
||||
//@BootLanguageServerTest
|
||||
@OverrideAutoConfiguration(enabled=false)
|
||||
@Import({LanguageServerAutoConf.class, XmlBeansTestConf.class})
|
||||
@SpringBootTest(classes={
|
||||
BootLanguageServerBootApp.class
|
||||
})
|
||||
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class SpringIndexerXMLProjectTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
|
||||
@@ -27,17 +27,22 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf;
|
||||
import org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoConf;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -46,8 +51,13 @@ import com.google.gson.Gson;
|
||||
* @author Alex Boyko
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(XmlBeansTestConf.class)
|
||||
//@BootLanguageServerTest
|
||||
@OverrideAutoConfiguration(enabled=false)
|
||||
@Import({LanguageServerAutoConf.class, XmlBeansTestConf.class})
|
||||
@SpringBootTest(classes={
|
||||
BootLanguageServerBootApp.class
|
||||
})
|
||||
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class XmlBeansHyperlinkTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
|
||||
@@ -31,8 +31,10 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
|
||||
import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf;
|
||||
import org.springframework.ide.vscode.boot.xml.SpringXMLReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
@@ -42,8 +44,11 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.starter.LanguageServerAutoConf;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -52,8 +57,13 @@ import com.google.gson.Gson;
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(XmlBeansTestConf.class)
|
||||
//@BootLanguageServerTest
|
||||
@OverrideAutoConfiguration(enabled=false)
|
||||
@Import({LanguageServerAutoConf.class, XmlBeansTestConf.class})
|
||||
@SpringBootTest(classes={
|
||||
BootLanguageServerBootApp.class
|
||||
})
|
||||
@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class XMLSpelExpressionValidationTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
|
||||
Reference in New Issue
Block a user