diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java index dfe6ba875..781fb7893 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/ProjectObserver.java @@ -16,7 +16,7 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; /** * Projects Observer. Able to add/remove project listeners which are notified on project changes - * + * * @author Alex Boyko * */ @@ -26,7 +26,7 @@ public interface ProjectObserver { void created(IJavaProject project); void changed(IJavaProject project); void deleted(IJavaProject project); - } + } void addListener(Listener listener); void removeListener(Listener listener); @@ -50,7 +50,7 @@ public interface ProjectObserver { public void created(IJavaProject project) { doit.accept(project); } - + @Override public void changed(IJavaProject project) { @@ -61,7 +61,7 @@ public interface ProjectObserver { public void deleted(IJavaProject project) { doit.accept(project); } - + }; } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/ComposableLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/ComposableLanguageServer.java index a8daca6c3..d4a340129 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/ComposableLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/composable/ComposableLanguageServer.java @@ -10,11 +10,12 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.composable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; 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.hover.VscodeHoverEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.VscodeHoverEngineAdapter.HoverType; -import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; 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 63ee6a691..7a939a9f7 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 @@ -18,9 +18,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -493,14 +495,27 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA return workspace; } + /** + * Keeps track of reconcile requests that have been requested but not yet started. + * This is used to more efficiently deal with situation where many requests are fired + * in a burst. Rather than execute the same request repeatedly we can avoid queuing + * up more requests if the previous request has not yet been started. + */ + private Set queuedReconcileRequests = Collections.synchronizedSet(new HashSet<>()); + /** * Convenience method. Subclasses can call this to use a {@link IReconcileEngine} ported * from old STS codebase to validate a given {@link TextDocument} and publish Diagnostics. */ public void validateWith(TextDocumentIdentifier docId, IReconcileEngine engine) { + if (!queuedReconcileRequests.add(docId)) { + return; + } + CompletableFuture reconcileSession = this.busyReconcile = new CompletableFuture(); // Log.debug("Reconciling BUSY"); + SimpleTextDocumentService documents = getTextDocumentService(); int requestedVersion = documents.getDocument(docId.getUri()).getVersion(); @@ -508,6 +523,7 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA // Avoid running in the same thread as lsp4j as it can result // in long "hangs" for slow reconcile providers Mono.fromRunnable(() -> { + queuedReconcileRequests.remove(docId); TextDocument doc = documents.getDocument(docId.getUri()).copy(); if (requestedVersion!=doc.getVersion()) { //Do not bother reconciling if document contents is already stale. diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java index cfa1121c8..9be363930 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleTextDocumentService.java @@ -16,7 +16,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.stream.Collectors; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServer.java index 0863bef58..04dea0566 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/BootLanguageServer.java @@ -10,15 +10,33 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents; import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer; import org.springframework.ide.vscode.commons.languageserver.composable.CompositeLanguageServerComponents; import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.util.text.TextDocument; public class BootLanguageServer extends ComposableLanguageServer { + private static final Logger log = LoggerFactory.getLogger(BootLanguageServer.class); + + private static ProjectObserver.Listener reconcileOpenDocuments(SimpleLanguageServer s, CompositeLanguageServerComponents c) { + return ProjectObserver.onAny(project -> { + c.getReconcileEngine().ifPresent(reconciler -> { + log.info("A project changed {}, triggering reconcile on all open documents", project.getElementName()); + for (TextDocument doc : s.getTextDocumentService().getAll()) { + s.validateWith(doc.getId(), reconciler); + } + }); + }); + } + private BootLanguageServer(String extensionId, LSFactory _components) { super(extensionId, _components); } @@ -26,13 +44,15 @@ public class BootLanguageServer extends Comp public static ComposableLanguageServer create(LSFactory _params) { return new ComposableLanguageServer<>("vscode-boot", s -> { BootLanguageServerParams params = _params.create(s); - CompositeLanguageServerComponents.Builder components = new CompositeLanguageServerComponents.Builder(); - components.add(new BootPropertiesLanguageServerComponents(s, (ignore) -> params)); - components.add(new BootJavaLanguageServerComponents(s, (ignore) -> params)); - return components.build(s); + CompositeLanguageServerComponents.Builder builder = new CompositeLanguageServerComponents.Builder(); + builder.add(new BootPropertiesLanguageServerComponents(s, (ignore) -> params)); + builder.add(new BootJavaLanguageServerComponents(s, (ignore) -> params)); + CompositeLanguageServerComponents components = builder.build(s); + params.projectObserver.addListener(reconcileOpenDocuments(s, components)); + return components; }); } - + public static ComposableLanguageServer createProperties(LSFactory params) { return new ComposableLanguageServer<>("vscode-boot-properties", s -> new BootPropertiesLanguageServerComponents(s, params)); }