Streamline async handler API

Added an async handler for LS requests that should not run on the same
thread as LSP4J event loop. Also streamlined method signatures for
handlers and removed useless CompletableFuture.
This commit is contained in:
nsingh
2018-03-12 16:07:43 -07:00
parent 591adec33d
commit 328607f531
39 changed files with 229 additions and 159 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016-2018 Pivotal, Inc.
* 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
@@ -11,17 +11,17 @@
package org.springframework.ide.vscode.commons.languageserver.completion;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import reactor.core.publisher.Mono;
/**
* Interface that needs to be implemented by a 'completion engine' which can be easily
* wired-up to provide completions for a Vscode language server.
*/
public interface VscodeCompletionEngine {
CompletableFuture<CompletionList> getCompletions(TextDocumentPositionParams params);
CompletableFuture<CompletionItem> resolveCompletion(CompletionItem unresolved);
Mono<CompletionList> getCompletions(TextDocumentPositionParams params);
CompletionItem resolveCompletion(CompletionItem unresolved);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016-2018 Pivotal, Inc.
* 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
@@ -16,7 +16,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.eclipse.lsp4j.CompletionItem;
@@ -115,8 +114,8 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
}
@Override
public CompletableFuture<CompletionList> getCompletions(TextDocumentPositionParams params) {
return getCompletionsMono(params).toFuture();
public Mono<CompletionList> getCompletions(TextDocumentPositionParams params) {
return getCompletionsMono(params);
}
private Mono<CompletionList> getCompletionsMono(TextDocumentPositionParams params) {
@@ -248,8 +247,8 @@ public class VscodeCompletionEngineAdapter implements VscodeCompletionEngine {
}
@Override
public CompletableFuture<CompletionItem> resolveCompletion(CompletionItem unresolved) {
public CompletionItem resolveCompletion(CompletionItem unresolved) {
resolver.resolveNow(unresolved);
return CompletableFuture.completedFuture(unresolved);
return unresolved;
}
}

View File

@@ -99,7 +99,7 @@ public class CompositeLanguageServerComponents implements LanguageServerComponen
//Create composite hover handler
this.hoverHandler = new HoverHandler() {
@Override
public CompletableFuture<Hover> handle(TextDocumentPositionParams params) {
public Hover handle(TextDocumentPositionParams params) {
TextDocument doc = server.getTextDocumentService().get(params);
LanguageId language = doc.getLanguageId();
LanguageServerComponents subComponents = componentsByLanguageId.get(language);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* 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
@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.commons.languageserver.definition;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Location;
@@ -36,10 +35,9 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
}
@Override
public CompletableFuture<List<Location>> handle(TextDocumentPositionParams position) {
public List<Location> handle(TextDocumentPositionParams position) {
return findDefinitions(position)
.collect(Collectors.toList())
.toFuture();
.collect(Collectors.toList()).block();
}
/**

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* Copyright (c) 2016, 2018 Pivotal, Inc.
* 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
@@ -59,7 +59,7 @@ public class VscodeHoverEngineAdapter implements HoverHandler {
}
@Override
public CompletableFuture<Hover> handle(TextDocumentPositionParams params) {
public Hover handle(TextDocumentPositionParams params) {
//TODO: This returns a CompletableFuture which suggests we should try to do expensive work asyncly.
// We are currently just doing all this in a blocking way and wrapping the already computed list into
// a trivial pre-resolved future.
@@ -78,7 +78,7 @@ public class VscodeHoverEngineAdapter implements HoverHandler {
String rendered = render(hoverInfo, type);
if (StringUtil.hasText(rendered)) {
Hover hover = new Hover(ImmutableList.of(Either.forLeft(rendered)), range);
return CompletableFuture.completedFuture(hover);
return hover;
}
}
}

View File

@@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import org.springframework.ide.vscode.commons.util.RunnableWithException;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
public class AsyncRunner {
private static Scheduler executor = Schedulers.newSingle("STS4 Thread");
// Only need to remember the last request as requests are executed in order, if
// the last request is done, all requests are done
private CompletableFuture<?> lastRequest;
public AsyncRunner() {
}
public synchronized <T> CompletableFuture<T> invoke(Callable<T> callable) {
CompletableFuture<T> x = Mono.fromCallable(callable).subscribeOn(executor).toFuture();
lastRequest = x;
return x;
}
public synchronized CompletableFuture<Void> execute(RunnableWithException runnable) {
CompletableFuture<Void> x = Mono.fromCallable(() -> {
runnable.run();
return (Void) null;
}).subscribeOn(executor).toFuture();
lastRequest = x;
return x;
}
public synchronized void waitForAll() {
while (lastRequest != null) {
try {
lastRequest.get();
} catch (Exception e) {
}
if (lastRequest.isDone()) {
lastRequest = null;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* 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
@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.CodeLensParams;
@@ -19,6 +18,6 @@ import org.eclipse.lsp4j.CodeLensParams;
@FunctionalInterface
public interface CodeLensHandler {
CompletableFuture<List<? extends CodeLens>> handle(CodeLensParams params);
List<? extends CodeLens> handle(CodeLensParams params);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* 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
@@ -10,11 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CodeLens;
@FunctionalInterface
public interface CodeLensResolveHandler {
CompletableFuture<CodeLens> handle(CodeLens unresolved);
CodeLens handle(CodeLens unresolved);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016-2018 Pivotal, Inc.
* 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
@@ -11,12 +11,12 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import reactor.core.publisher.Mono;
@FunctionalInterface
public interface CompletionHandler {
CompletableFuture<CompletionList> handle(TextDocumentPositionParams params);
Mono<CompletionList> handle(TextDocumentPositionParams params);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016-2018 Pivotal, Inc.
* 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
@@ -11,11 +11,9 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionItem;
@FunctionalInterface
public interface CompletionResolveHandler {
CompletableFuture<CompletionItem> handle(CompletionItem unresolved);
CompletionItem handle(CompletionItem unresolved) throws Exception;
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* 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
@@ -11,12 +11,11 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.TextDocumentPositionParams;
@FunctionalInterface
public interface DefinitionHandler {
CompletableFuture<List<Location>> handle(TextDocumentPositionParams position);
List<Location> handle(TextDocumentPositionParams position);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Pivotal, Inc.
* Copyright (c) 2016-2018 Pivotal, Inc.
* 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
@@ -11,12 +11,10 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.TextDocumentPositionParams;
@FunctionalInterface
public interface HoverHandler {
CompletableFuture<Hover> handle(TextDocumentPositionParams params);
Hover handle(TextDocumentPositionParams params);
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* Copyright (c) 2017, 2018 Pivotal, Inc.
* 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
@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.commons.languageserver.util;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.ReferenceParams;
@@ -19,6 +18,6 @@ import org.eclipse.lsp4j.ReferenceParams;
@FunctionalInterface
public interface ReferencesHandler {
CompletableFuture<List<? extends Location>> handle(ReferenceParams params);
List<? extends Location> handle(ReferenceParams params);
}

View File

@@ -128,6 +128,8 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
private Runnable shutdownHandler;
private AsyncRunner async = new AsyncRunner();
@Override
public void connect(LanguageClient _client) {
this.client = (STS4LanguageClient) _client;
@@ -614,4 +616,7 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
this.shutdownHandler = handler;
}
public AsyncRunner getAsync() {
return this.async;
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016, 2017 Pivotal, Inc.
* Copyright (c) 2016, 2018 Pivotal, Inc.
* 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
@@ -56,15 +56,13 @@ import org.eclipse.lsp4j.services.TextDocumentService;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
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;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class SimpleTextDocumentService implements TextDocumentService {
final private SimpleLanguageServer server;
@@ -84,9 +82,11 @@ public class SimpleTextDocumentService implements TextDocumentService {
private CodeLensResolveHandler codeLensResolveHandler;
private Consumer<TextDocumentSaveChange> documentSaveListener;
private AsyncRunner async;
public SimpleTextDocumentService(SimpleLanguageServer server) {
this.server = server;
this.async = server.getAsync();
}
public synchronized void onHover(HoverHandler h) {
@@ -141,6 +141,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
@Override
public final void didChange(DidChangeTextDocumentParams params) {
async.execute(() -> {
try {
VersionedTextDocumentIdentifier docId = params.getTextDocument();
String url = docId.getUri();
@@ -154,10 +155,12 @@ public class SimpleTextDocumentService implements TextDocumentService {
} catch (BadLocationException e) {
Log.log(e);
}
});
}
@Override
public void didOpen(DidOpenTextDocumentParams params) {
async.execute(() -> {
TextDocumentItem docId = params.getTextDocument();
String url = docId.getUri();
//Log.info("didOpen: "+params.getTextDocument().getUri());
@@ -187,10 +190,12 @@ public class SimpleTextDocumentService implements TextDocumentService {
TextDocumentContentChange evt = new TextDocumentContentChange(doc, ImmutableList.of(change));
documentChangeListeners.fire(evt);
}
});
}
@Override
public void didClose(DidCloseTextDocumentParams params) {
async.execute(() -> {
//Log.info("didClose: "+params.getTextDocument().getUri());
String url = params.getTextDocument().getUri();
if (url!=null) {
@@ -211,6 +216,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
Log.warn("Document closed, but it didn't exist! Close event ignored");
}
}
});
}
void didChangeContent(TextDocument doc, List<TextDocumentContentChangeEvent> changes) {
@@ -250,37 +256,42 @@ public class SimpleTextDocumentService implements TextDocumentService {
}
public final static CompletionList NO_COMPLETIONS = new CompletionList(false, Collections.emptyList());
public final static CompletableFuture<Hover> NO_HOVER = CompletableFuture.completedFuture(new Hover(ImmutableList.of(), null));
public final static CompletableFuture<List<? extends Location>> NO_REFERENCES = CompletableFuture.completedFuture(ImmutableList.of());
public final static Hover NO_HOVER = new Hover(ImmutableList.of(), null);
public final static List<? extends Location> NO_REFERENCES = ImmutableList.of();
public final static List<? extends SymbolInformation> NO_SYMBOLS = ImmutableList.of();
public final static CompletableFuture<List<? extends CodeLens>> NO_CODELENS = CompletableFuture.completedFuture(ImmutableList.of());
public final static List<? extends CodeLens> NO_CODELENS = ImmutableList.of();
@Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(TextDocumentPositionParams position) {
CompletionHandler h = completionHandler;
if (h!=null) {
return completionHandler.handle(position)
.thenApply(Either::forRight);
.map(Either::<List<CompletionItem>, CompletionList>forRight)
.toFuture();
}
return CompletableFuture.completedFuture(Either.forRight(NO_COMPLETIONS));
}
@Override
public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem unresolved) {
return async.invoke(() -> {
CompletionResolveHandler h = completionResolveHandler;
if (h!=null) {
return h.handle(unresolved);
}
return null;
});
}
@Override
public CompletableFuture<Hover> hover(TextDocumentPositionParams position) {
return async.invoke(() -> {
HoverHandler h = hoverHandler;
if (h!=null) {
return hoverHandler.handle(position);
}
return CompletableFuture.completedFuture(null);
return null;
});
}
@Override
@@ -288,62 +299,62 @@ public class SimpleTextDocumentService implements TextDocumentService {
return CompletableFuture.completedFuture(null);
}
@SuppressWarnings({ "unchecked"})
@Override
public CompletableFuture<List<? extends Location>> definition(TextDocumentPositionParams position) {
return async.invoke(() -> {
DefinitionHandler h = this.definitionHandler;
if (h!=null) {
Object r = h.handle(position); //YUCK!
return (CompletableFuture<List<? extends Location>>) r;
return h.handle(position);
}
return CompletableFuture.completedFuture(Collections.emptyList());
return Collections.emptyList();
});
}
@Override
public CompletableFuture<List<? extends Location>> references(ReferenceParams params) {
return async.invoke(() -> {
ReferencesHandler h = this.referencesHandler;
if (h != null) {
return h.handle(params);
}
return CompletableFuture.completedFuture(Collections.emptyList());
return Collections.emptyList();
});
}
@Override
public CompletableFuture<List<? extends SymbolInformation>> documentSymbol(DocumentSymbolParams params) {
return async.invoke(() -> {
DocumentSymbolHandler documentSymbolHandler = this.documentSymbolHandler;
if (documentSymbolHandler==null) {
return CompletableFuture.completedFuture(ImmutableList.of());
return ImmutableList.of();
}
return Mono.fromCallable(() -> {
server.waitForReconcile();
List<? extends SymbolInformation> r = documentSymbolHandler.handle(params);
//handle it when symbolHandler is sloppy and returns null instead of empty list.
return r == null ? ImmutableList.of() : r;
})
.toFuture()
.thenApply(l -> (List<? extends SymbolInformation>)l);
server.waitForReconcile();
List<? extends SymbolInformation> r = documentSymbolHandler.handle(params);
//handle it when symbolHandler is sloppy and returns null instead of empty list.
return r == null ? ImmutableList.of() : r;
});
}
@Override
public CompletableFuture<List<? extends Command>> codeAction(CodeActionParams params) {
return async.invoke(() -> {
TrackedDocument doc = documents.get(params.getTextDocument().getUri());
if (doc!=null) {
return Flux.fromIterable(doc.getQuickfixes())
return doc.getQuickfixes().stream()
.filter((fix) -> fix.appliesTo(params.getRange(), params.getContext()))
.map(Quickfix::getCodeAction)
.collectList()
.toFuture()
.thenApply(l -> (List<? extends Command>) l);
.collect(CollectorUtil.toImmutableList());
} else {
return CompletableFuture.completedFuture(ImmutableList.of());
return ImmutableList.of();
}
});
}
@Override
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams params) {
CodeLensHandler handler = this.codeLensHandler;
if (handler != null) {
return handler.handle(params);
return async.invoke(() -> handler.handle(params));
}
return CompletableFuture.completedFuture(Collections.emptyList());
}
@@ -352,7 +363,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
public CompletableFuture<CodeLens> resolveCodeLens(CodeLens unresolved) {
CodeLensResolveHandler handler = this.codeLensResolveHandler;
if (handler != null) {
return handler.handle(unresolved);
return async.invoke(() -> handler.handle(unresolved));
}
return CompletableFuture.completedFuture(null);
}
@@ -385,6 +396,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
// which extends the YEdit editor. This YEdit editor has a problem, where on save, all error markers are deleted.
// When STS uses the LSP4E editor and no longer needs its own YEdit-based editor, the issue with error markers disappearing
// on save should not be a problem anymore, and the workaround below will no longer be needed.
async.execute(() -> {
if (documentSaveListener != null) {
TextDocumentIdentifier docId = params.getTextDocument();
String url = docId.getUri();
@@ -394,6 +406,7 @@ public class SimpleTextDocumentService implements TextDocumentService {
documentSaveListener.accept(new TextDocumentSaveChange(doc));
}
}
});
}
public void publishDiagnostics(TextDocumentIdentifier docId, Collection<Diagnostic> diagnostics) {

View File

@@ -34,8 +34,6 @@ import org.springframework.ide.vscode.commons.util.Log;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Mono;
public class SimpleWorkspaceService implements WorkspaceService {
private static Logger log = LoggerFactory.getLogger(SimpleWorkspaceService.class);
@@ -50,24 +48,25 @@ public class SimpleWorkspaceService implements WorkspaceService {
private ListenerList<DidChangeWorkspaceFoldersParams> workspaceFolderListeners = new ListenerList<>();
private AsyncRunner async;
public SimpleWorkspaceService(SimpleLanguageServer server) {
this.server = server;
this.async = server.getAsync();
this.fileObserver = new SimpleServerFileObserver(server);
}
@Override
public CompletableFuture<List<? extends SymbolInformation>> symbol(WorkspaceSymbolParams params) {
return async.invoke(() -> {
WorkspaceSymbolHandler workspaceSymbolHandler = this.workspaceSymbolHandler;
if (workspaceSymbolHandler==null) {
return CompletableFuture.completedFuture(ImmutableList.of());
return ImmutableList.of();
}
return Mono.fromCallable(() -> {
server.waitForReconcile();
List<? extends SymbolInformation> symbols = workspaceSymbolHandler.handle(params);
return symbols==null ? ImmutableList.of() : symbols;
})
.toFuture()
.thenApply(l -> (List<? extends SymbolInformation>)l);
server.waitForReconcile();
List<? extends SymbolInformation> symbols = workspaceSymbolHandler.handle(params);
return symbols == null ? ImmutableList.of() : symbols;
});
}
@Override

View File

@@ -16,7 +16,6 @@ import java.nio.file.Path;
import org.springframework.ide.vscode.commons.java.AbstractJavaProject;
import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache;
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.util.Log;
@@ -31,20 +30,20 @@ public class MavenJavaProject extends AbstractJavaProject {
private DelegatingCachedClasspath<MavenProjectClasspath> classpath;
private File pom;
public MavenJavaProject(STS4LanguageClient client, MavenCore maven, File pom, Path projectDataCache) {
public MavenJavaProject(MavenCore maven, File pom, Path projectDataCache) {
super(projectDataCache);
this.pom = pom;
File file = projectDataCache == null ? null
: projectDataCache.resolve(ClasspathFileBasedCache.CLASSPATH_DATA_CACHE_FILE).toFile();
ClasspathFileBasedCache fileBasedCache = new ClasspathFileBasedCache(file);
this.classpath = new DelegatingCachedClasspath<>(
() -> new MavenProjectClasspath(client, maven, pom),
() -> new MavenProjectClasspath(maven, pom),
fileBasedCache
);
}
public MavenJavaProject(STS4LanguageClient client, MavenCore maven, File pom) {
this(client, maven, pom, null);
public MavenJavaProject(MavenCore maven, File pom) {
this(maven, pom, null);
if (!classpath.isCached()) {
try {
classpath.update();

View File

@@ -15,11 +15,9 @@ import java.nio.file.Path;
import org.eclipse.lsp4j.MessageParams;
import org.eclipse.lsp4j.MessageType;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.Sts4LanguageServer;
import org.springframework.ide.vscode.commons.languageserver.java.AbstractFileToProjectCache;
import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.maven.MavenCore;
/**
@@ -50,7 +48,6 @@ public class MavenProjectCache extends AbstractFileToProjectCache<MavenJavaProje
@Override
protected MavenJavaProject createProject(File pomFile) throws Exception {
MavenJavaProject mavenJavaProject = new MavenJavaProject(
getClient(),
maven,
pomFile,
projectCacheFolder == null ? null : pomFile.getParentFile().toPath().resolve(projectCacheFolder)
@@ -58,9 +55,4 @@ public class MavenProjectCache extends AbstractFileToProjectCache<MavenJavaProje
performUpdate(mavenJavaProject, asyncUpdate, asyncUpdate);
return mavenJavaProject;
}
private STS4LanguageClient getClient() {
return ((SimpleLanguageServer) server).getClient();
}
}

View File

@@ -33,8 +33,6 @@ import org.springframework.ide.vscode.commons.java.ClasspathData;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.maven.MavenCore;
import org.springframework.ide.vscode.commons.maven.MavenException;
import org.springframework.ide.vscode.commons.util.Log;
@@ -53,11 +51,9 @@ public class MavenProjectClasspath extends JandexClasspath {
private MavenCore maven;
private File pom;
private MavenClasspathData cachedData;
private STS4LanguageClient client;
MavenProjectClasspath(STS4LanguageClient client, MavenCore maven, File pom) throws Exception {
MavenProjectClasspath(MavenCore maven, File pom) throws Exception {
super();
this.client = client;
this.maven = maven;
this.pom = pom;
this.cachedData = createClasspathData();
@@ -98,7 +94,6 @@ public class MavenProjectClasspath extends JandexClasspath {
// return Stream.concat(maven.resolveDependencies(project, null).stream().map(artifact -> {
// return artifact.getFile().toPath();
// }), projectResolvedOutput());
Object classpath = client.classpath(new ClasspathParams(pom.toURI().toString())).get();
ImmutableList<Path> classpathEntries = ImmutableList.copyOf(Stream.concat(projectDependencies(project).stream().map(a -> a.getFile().toPath()),
projectOutput(project).stream().map(f -> f.toPath())).collect(Collectors.toList()));
return classpathEntries;

View File

@@ -40,7 +40,7 @@ public class HtmlJavadocTest {
JandexClasspath.providerType = JavadocProviderTypes.HTML;
testProjectPath = Paths.get(HtmlJavadocTest.class.getResource("/gs-rest-service-cors-boot-1.4.1-with-classpath-file").toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
} catch (Exception e) {
return null;
}

View File

@@ -47,7 +47,7 @@ public class JavaIndexTest {
public MavenJavaProject load(String projectName) throws Exception {
Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/" + projectName).toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
}
});

View File

@@ -0,0 +1,17 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* 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:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.util;
@FunctionalInterface
public interface RunnableWithException {
void run() throws Exception;
}

View File

@@ -140,7 +140,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
this.defaultLanguageId = defaultLanguageId;
}
public static final Duration HIGHLIGHTS_TIMEOUT = Duration.ofMillis(1_000); //TODO: why does it need to be this long, that's fishy!
public static final Duration HIGHLIGHTS_TIMEOUT = Duration.ofMillis(3_000);
public LanguageServerHarness(Callable<S> factory) throws Exception {
this(factory, LanguageId.PLAINTEXT);
@@ -421,7 +421,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
}
public PublishDiagnosticsParams getDiagnostics(TextDocumentInfo doc) throws Exception {
this.getServer().waitForReconcile();
waitForReconcile();
return diagnostics.get(doc.getUri());
}
@@ -468,7 +468,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
TextDocumentPositionParams params = new TextDocumentPositionParams();
params.setPosition(cursor);
params.setTextDocument(doc.getId());
getServer().waitForReconcile();
waitForReconcile();
Either<List<CompletionItem>, CompletionList> completions = getServer().getTextDocumentService().completion(params).get();
if (completions.isLeft()) {
List<CompletionItem> list = completions.getLeft();
@@ -478,6 +478,11 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
}
}
private void waitForReconcile() throws Exception {
getServer().getAsync().waitForAll();
getServer().waitForReconcile();
}
public Hover getHover(TextDocumentInfo document, Position cursor) throws Exception {
TextDocumentPositionParams params = new TextDocumentPositionParams();
@@ -594,7 +599,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
}
public List<? extends Location> getDefinitions(TextDocumentPositionParams params) throws Exception {
getServer().waitForReconcile(); //goto definitions relies on reconciler infos! Must wait or race condition breaking tests occasionally.
waitForReconcile(); //goto definitions relies on reconciler infos! Must wait or race condition breaking tests occasionally.
return getServer().getTextDocumentService().definition(params).get();
}
@@ -661,7 +666,7 @@ public class LanguageServerHarness<S extends SimpleLanguageServerWrapper> {
}
public List<? extends SymbolInformation> getDocumentSymbols(TextDocumentInfo document) throws Exception {
getServer().waitForReconcile(); //TODO: if the server works properly this shouldn't be needed it should do that internally itself somehow.
waitForReconcile(); //TODO: if the server works properly this shouldn't be needed it should do that internally itself somehow.
DocumentSymbolParams params = new DocumentSymbolParams(document.getId());
return getServer().getTextDocumentService().documentSymbol(params).get();
}

View File

@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.concourse;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.DiagnosticSeverity;
@@ -44,6 +43,8 @@ import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Mono;
public class ConcourseLanguageServer extends SimpleLanguageServer {
private final YamlCompletionEngineOptions COMPLETION_OPTIONS;
@@ -134,11 +135,11 @@ public class ConcourseLanguageServer extends SimpleLanguageServer {
return forTasks.completionEngine.getCompletions(params);
}
}
return CompletableFuture.completedFuture(new CompletionList(false, ImmutableList.of()));
return Mono.just(new CompletionList(false, ImmutableList.of()));
});
documents.onCompletionResolve(item -> {
completionResolver.resolveNow(item);
return CompletableFuture.completedFuture(item);
return item;
});
documents.onHover(params -> {
TextDocument doc = documents.get(params);

View File

@@ -58,7 +58,7 @@ public class ResolveClasspathHandler implements IDelegateCommandHandler {
}
}
Classpath classpath = new Classpath(cpEntries, javaProject.getOutputLocation().toString());
// log("classpath=" + classpath);
log("classpath=" + classpath);
return classpath;
}

View File

@@ -28,7 +28,9 @@ public class ResolveProjectHandler implements IDelegateCommandHandler {
log("ResolveProjectHandler=" + commandId);
try {
URI resourceUri = ResourceUtils.getResourceUri(arguments);
log("resourceUri=" + resourceUri);
IJavaProject javaProject = ResourceUtils.getJavaProject(resourceUri);
ProjectResponse projectResponse = new ProjectResponse(javaProject.getElementName(), javaProject.getProject().getLocationURI().toString());

View File

@@ -36,14 +36,14 @@ public class BootJavaCodeLensEngine implements CodeLensHandler {
}
@Override
public CompletableFuture<List<? extends CodeLens>> handle(CodeLensParams params) {
public List<? extends CodeLens> handle(CodeLensParams params) {
SimpleTextDocumentService documents = server.getTextDocumentService();
String docURI = params.getTextDocument().getUri();
if (documents.get(docURI) != null) {
TextDocument doc = documents.get(docURI).copy();
try {
CompletableFuture<List<? extends CodeLens>> codeLensesResult = provideCodeLenses(doc);
List<? extends CodeLens> codeLensesResult = provideCodeLenses(doc);
if (codeLensesResult != null) {
return codeLensesResult;
}
@@ -55,7 +55,7 @@ public class BootJavaCodeLensEngine implements CodeLensHandler {
return SimpleTextDocumentService.NO_CODELENS;
}
private CompletableFuture<List<? extends CodeLens>> provideCodeLenses(TextDocument document) {
private List<? extends CodeLens> provideCodeLenses(TextDocument document) {
return server.getCompilationUnitCache().withCompilationUnit(document, cu -> {
if (cu != null) {
@@ -65,7 +65,7 @@ public class BootJavaCodeLensEngine implements CodeLensHandler {
}
if (result.size() > 0) {
return CompletableFuture.completedFuture(result);
return result;
}
}

View File

@@ -64,7 +64,7 @@ public class BootJavaHoverProvider implements HoverHandler {
}
@Override
public CompletableFuture<Hover> handle(TextDocumentPositionParams params) {
public Hover handle(TextDocumentPositionParams params) {
SimpleTextDocumentService documents = server.getTextDocumentService();
if (documents.get(params) != null) {
TextDocument doc = documents.get(params).copy();
@@ -72,7 +72,7 @@ public class BootJavaHoverProvider implements HoverHandler {
int offset = doc.toOffset(params.getPosition());
Hover hoverResult = provideHover(doc, offset);
if (hoverResult != null) {
return CompletableFuture.completedFuture(hoverResult);
return hoverResult;
}
}
catch (Exception e) {

View File

@@ -52,13 +52,13 @@ public class BootJavaReferencesHandler implements ReferencesHandler {
}
@Override
public CompletableFuture<List<? extends Location>> handle(ReferenceParams params) {
public List<? extends Location> handle(ReferenceParams params) {
SimpleTextDocumentService documents = server.getTextDocumentService();
TextDocument doc = documents.get(params).copy();
if (doc != null) {
try {
int offset = doc.toOffset(params.getPosition());
CompletableFuture<List<? extends Location>> referencesResult = provideReferences(doc, offset);
List<? extends Location> referencesResult = provideReferences(doc, offset);
if (referencesResult != null) {
return referencesResult;
}
@@ -70,7 +70,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler {
return SimpleTextDocumentService.NO_REFERENCES;
}
private CompletableFuture<List<? extends Location>> provideReferences(TextDocument document, int offset) throws Exception {
private List<? extends Location> provideReferences(TextDocument document, int offset) throws Exception {
ASTParser parser = ASTParser.newParser(AST.JLS9);
Map<String, String> options = JavaCore.getOptions();
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
@@ -99,7 +99,7 @@ public class BootJavaReferencesHandler implements ReferencesHandler {
return null;
}
private CompletableFuture<List<? extends Location>> provideReferencesForAnnotation(ASTNode node, int offset, TextDocument doc) {
private List<? extends Location> provideReferencesForAnnotation(ASTNode node, int offset, TextDocument doc) {
Annotation annotation = null;
while (node != null && !(node instanceof Annotation)) {

View File

@@ -11,7 +11,6 @@
package org.springframework.ide.vscode.boot.java.handlers;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
@@ -24,7 +23,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
public interface ReferenceProvider {
CompletableFuture<List<? extends Location>> provideReferences(ASTNode node, Annotation annotation,
List<? extends Location> provideReferences(ASTNode node, Annotation annotation,
ITypeBinding type, int offset, TextDocument doc);
}

View File

@@ -63,7 +63,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider {
}
@Override
public CompletableFuture<List<? extends Location>> provideReferences(ASTNode node, Annotation annotation,
public List<? extends Location> provideReferences(ASTNode node, Annotation annotation,
ITypeBinding type, int offset, TextDocument doc) {
try {
@@ -88,7 +88,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider {
return null;
}
private CompletableFuture<List<? extends Location>> provideReferences(String value, int offset, int nodeStartOffset, TextDocument doc) {
private List<? extends Location> provideReferences(String value, int offset, int nodeStartOffset, TextDocument doc) {
try {
LocalRange range = getPropertyRange(value, offset);
@@ -106,7 +106,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider {
return null;
}
public CompletableFuture<List<? extends Location>> findReferencesFromPropertyFiles(
public List<? extends Location> findReferencesFromPropertyFiles(
Collection<WorkspaceFolder> workspaceRoots,
String propertyKey
) {
@@ -121,7 +121,7 @@ public class ValuePropertyReferencesProvider implements ReferenceProvider {
.flatMap(Collection::stream)
.collect(Collectors.toList());
return CompletableFuture.completedFuture(locations);
return locations;
}
} catch (Exception e) {
e.printStackTrace();

View File

@@ -11,15 +11,12 @@
package org.springframework.ide.vscode.boot.jdt.ls;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.slf4j.Logger;
@@ -29,9 +26,9 @@ import org.springframework.ide.vscode.commons.java.ClasspathData;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.ClasspathResponse;
import org.springframework.ide.vscode.commons.languageserver.ProjectResponse;
import org.springframework.ide.vscode.commons.languageserver.ClasspathParams;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;

View File

@@ -38,10 +38,9 @@ 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(wsFolder(root), "test.property");
List<? extends Location> locations = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
assertNotNull(locations);
assertEquals(1, locations.size());
Location location = locations.get(0);
@@ -68,10 +67,9 @@ public class PropertyReferenceFinderTest {
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(wsFolder(root), "test.property");
List<? extends Location> locations = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
assertNotNull(locations);
assertEquals(1, locations.size());
Location location = locations.get(0);
@@ -88,10 +86,9 @@ 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(wsFolder(root), "server.port");
List<? extends Location> locations = provider.findReferencesFromPropertyFiles(wsFolder(root), "server.port");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
assertNotNull(locations);
assertEquals(1, locations.size());
Location location = locations.get(0);
@@ -108,10 +105,9 @@ 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(wsFolder(root), "appl1.prop");
List<? extends Location> locations = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
assertNotNull(locations);
assertEquals(3, locations.size());
Location location = getLocation(locations, Paths.get(root.toString(), "application-dev.properties").toUri());
@@ -151,10 +147,9 @@ 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(wsFolder(root), "appl1.prop");
List<? extends Location> locations = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
assertNotNull(resultFuture);
List<? extends Location> locations = resultFuture.get();
assertNotNull(locations);
assertEquals(2, locations.size());
Location location = getLocation(locations, Paths.get(root.toString(), "application-dev.properties").toUri());

View File

@@ -86,6 +86,7 @@ public class CompilationUnitCacheTest {
}
private CompilationUnit getCompilationUnit(TextDocument doc) {
harness.getServerWrapper().getServer().getAsync().waitForAll();
return harness.getServerWrapper().getComponents().getCompilationUnitCache().withCompilationUnit(doc, cu -> cu);
}

View File

@@ -43,7 +43,7 @@ public class VSCodeSourceLinksTest {
public MavenJavaProject load(String projectName) throws Exception {
Path testProjectPath = Paths.get(VSCodeSourceLinksTest.class.getResource("/test-projects/" + projectName).toURI());
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
}
});

View File

@@ -101,7 +101,7 @@ public class ProjectsHarness {
switch (type) {
case MAVEN:
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
return new MavenJavaProject(null, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
case CLASSPATH_TXT:
MavenBuilder.newBuilder(testProjectPath).clean().pack().skipTests().execute();
return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile());

View File

@@ -15,8 +15,8 @@ import {HighlightService, HighlightParams} from './highlight-service';
import { log } from 'util';
import { tmpdir } from 'os';
import { JVM, findJvm, findJdk } from '@pivotal-tools/jvm-launch-utils';
import { registerClasspathService } from './classpath-service';
import { registerProjectService } from './project-service';
import { registerClasspathService } from './classpath';
import { registerProjectService } from './project';
let p2c = P2C.createConverter();
@@ -48,11 +48,12 @@ function getUserDefinedJvmHeap(wsOpts : VSCode.WorkspaceConfiguration, dflt : s
return javaOptions.heap || dflt;
}
export function activate(options: ActivatorOptions, context: VSCode.ExtensionContext): Promise<LanguageClient> {
export function activate(options: ActivatorOptions, context: VSCode.ExtensionContext): Thenable<LanguageClient> {
let DEBUG = options.DEBUG;
let jvmHeap = getUserDefinedJvmHeap(options.workspaceOptions, options.jvmHeap);
if (options.CONNECT_TO_LS) {
return connectToLS(context, options);
return VSCode.window.showInformationMessage("Start language server")
.then((x) => connectToLS(context, options));
} else {
let clientOptions = options.clientOptions;

View File

@@ -7,8 +7,8 @@ import { LanguageClient, RequestType } from 'vscode-languageclient';
export function registerProjectService(client : LanguageClient) : void {
let projectRequest = new RequestType<string, ProjectResponse, void, void>("sts/project");
client.onRequest(projectRequest, async (uri: string) => {
return await executeProjectCommand(uri);
client.onRequest(projectRequest, (uri: string) => {
return executeProjectCommand(uri);
});
}