diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 6fe9b8e3e..671801e09 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -40,6 +40,10 @@ import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Futures; import org.springframework.ide.vscode.commons.util.text.TextDocument; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + /** * Abstract base class to implement LanguageServer. Bits and pieces copied from * the 'JavaLanguageServer' example which seem generally useful / reusable end up in @@ -50,6 +54,8 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl private static final Logger LOG = Logger.getLogger(SimpleLanguageServer.class.getName()); + private static final Scheduler RECONCILER_SCHEDULER = Schedulers.newSingle("Reconciler"); + private Path workspaceRoot; private SimpleTextDocumentService tds; @@ -65,6 +71,8 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl } }; + private CompletableFuture busyReconcile = CompletableFuture.completedFuture(null); + @Override public void connect(LanguageClient _client) { this.client = (STS4LanguageClient) _client; @@ -150,7 +158,10 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl * 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. */ - protected void validateWith(TextDocument doc, IReconcileEngine engine) { + protected void validateWith(TextDocument _doc, IReconcileEngine engine) { + TextDocument doc = _doc.copy(); + + this.busyReconcile = new CompletableFuture(); SimpleTextDocumentService documents = getTextDocumentService(); IProblemCollector problems = new IProblemCollector() { @@ -198,7 +209,23 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl } } }; - engine.reconcile(doc, problems); + + // Avoid running in the same thread as lsp4j as it can result + // in long "hangs" for slow reconcile providers + Mono.fromRunnable(() -> { + engine.reconcile(doc, problems); + }) + .doOnTerminate((ignore1, ignore2) -> { + busyReconcile.complete(null); + }) + .subscribeOn(RECONCILER_SCHEDULER) + .subscribe(); + } + + public void waitForReconcile() throws Exception { + while (!this.busyReconcile.isDone()) { + this.busyReconcile.get(); + } } public LanguageClient getClient() { diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java index 20ea262e3..b87bb6334 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/Editor.java @@ -221,7 +221,7 @@ public class Editor { : ""; } - private List reconcile() { + private List reconcile() throws Exception { // We assume the language server works synchronously for now and it does an immediate reconcile // when the document changes. In the future this is probably not going to be the case though and then this // method will need to somehow ensure the linter is done working before retrieving the problems from the diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 3232da307..1294557a3 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -54,6 +54,7 @@ import org.eclipse.lsp4j.services.LanguageServer; import org.springframework.ide.vscode.commons.languageserver.LanguageIds; import org.springframework.ide.vscode.commons.languageserver.ProgressParams; import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; public class LanguageServerHarness { @@ -61,10 +62,10 @@ public class LanguageServerHarness { private Random random = new Random(); - private Callable factory; + private Callable factory; private String defaultLanguageId; - private LanguageServer server; + private SimpleLanguageServer server; private InitializeResult initResult; @@ -72,12 +73,12 @@ public class LanguageServerHarness { private Map diagnostics = new HashMap<>(); - public LanguageServerHarness(Callable factory, String defaultLanguageId) { + public LanguageServerHarness(Callable factory, String defaultLanguageId) { this.factory = factory; this.defaultLanguageId = defaultLanguageId; } - public LanguageServerHarness(Callable factory) throws Exception { + public LanguageServerHarness(Callable factory) throws Exception { this(factory, LanguageIds.PLAINTEXT); } @@ -263,7 +264,8 @@ public class LanguageServerHarness { return TextDocumentSyncKind.None; } - public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) { + public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) throws Exception { + this.server.waitForReconcile(); return diagnostics.get(doc.getUri()); }