PT #166603176: Limit hover request processing time

This commit is contained in:
BoykoAlex
2019-06-21 14:01:53 -04:00
parent cd873229b6
commit ee16ee17c3
12 changed files with 1604 additions and 2626 deletions

View File

@@ -119,6 +119,8 @@ public abstract class STS4LanguageServerProcessStreamConnector extends ProcessSt
command.add(mainClass);
command.add("--languageserver.hover-timeout=225");
setCommands(command.build());
}
catch (Exception e) {

View File

@@ -34,6 +34,11 @@ public class LanguageServerProperties {
* triggers.
*/
private Map<String, String> completionTriggerCharacters;
/**
* Hover request handler timeout in milliseconds
*/
private long hoverTimeout = -1;
public boolean isStandalone() {
return standalone;
@@ -67,4 +72,12 @@ public class LanguageServerProperties {
this.completionTriggerCharacters = completionTriggerCharacters;
}
public long getHoverTimeout() {
return hoverTimeout;
}
public void setHoverTimeout(long hoverTimeout) {
this.hoverTimeout = hoverTimeout;
}
}

View File

@@ -63,6 +63,7 @@ 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.completion.VscodeCompletionEngineAdapter.CompletionFilter;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.LazyCompletionResolver;
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
import org.springframework.ide.vscode.commons.languageserver.java.ls.ClasspathListener;
import org.springframework.ide.vscode.commons.languageserver.java.ls.ClasspathListenerManager;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
@@ -114,6 +115,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
private SimpleTextDocumentService tds;
private SimpleWorkspaceService workspace;
private STS4LanguageClient client;
private final LanguageServerProperties props;
private ProgressService progressService = (String taskId, String statusMsg) -> {
STS4LanguageClient client = SimpleLanguageServer.this.client;
@@ -187,8 +189,9 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
}
}
public SimpleLanguageServer(String extensionId, ApplicationContext appContext) {
public SimpleLanguageServer(String extensionId, ApplicationContext appContext, LanguageServerProperties props) {
this.appContext = appContext;
this.props = props;
Assert.isNotNull(extensionId);
this.EXTENSION_ID = extensionId;
this.CODE_ACTION_COMMAND_ID = "sts."+EXTENSION_ID+".codeAction";
@@ -485,7 +488,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC
}
protected SimpleTextDocumentService createTextDocumentService() {
return new SimpleTextDocumentService(this);
return new SimpleTextDocumentService(this, props);
}
public SimpleWorkspaceService createWorkspaceService() {

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.util;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -59,6 +60,7 @@ import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.AsyncRunner;
@@ -70,11 +72,14 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Mono;
public class SimpleTextDocumentService implements TextDocumentService, DocumentEventListenerManager {
private static Logger log = LoggerFactory.getLogger(SimpleTextDocumentService.class);
final private SimpleLanguageServer server;
final private LanguageServerProperties props;
private Map<String, TrackedDocument> documents = new HashMap<>();
private ListenerList<TextDocumentContentChange> documentChangeListeners = new ListenerList<>();
private ListenerList<TextDocument> documentCloseListeners = new ListenerList<>();
@@ -97,8 +102,9 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
private AsyncRunner async;
public SimpleTextDocumentService(SimpleLanguageServer server) {
public SimpleTextDocumentService(SimpleLanguageServer server, LanguageServerProperties props) {
this.server = server;
this.props = props;
this.async = server.getAsync();
}
@@ -324,19 +330,24 @@ public class SimpleTextDocumentService implements TextDocumentService, DocumentE
@Override
public CompletableFuture<Hover> hover(TextDocumentPositionParams position) {
log.debug("hover requested for {}", position);
return async.invoke(() -> {
try {
log.debug("hover handler starting");
HoverHandler h = hoverHandler;
if (h!=null) {
return hoverHandler.handle(position);
}
log.debug("no hover because there is no handler");
return null;
} finally {
log.debug("hover handler finished");
long timeout = props.getHoverTimeout();
return timeout <= 0 ? async.invoke(() -> computeHover(position)) : async.invoke(Duration.ofMillis(timeout), () -> computeHover(position), Mono.fromRunnable(() -> {
log.error("Hover Request handler timed out after {} ms.", timeout);
}));
}
private Hover computeHover(TextDocumentPositionParams position) {
try {
log.debug("hover handler starting");
HoverHandler h = hoverHandler;
if (h!=null) {
return hoverHandler.handle(position);
}
});
log.debug("no hover because there is no handler");
return null;
} finally {
log.debug("hover handler finished");
}
}
@Override

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* Copyright (c) 2018, 2019 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
@@ -41,6 +41,15 @@ public class AsyncRunner {
return x;
}
public synchronized <T> CompletableFuture<T> invoke(Duration timeout, Callable<T> callable, Mono<T> fallback) {
CompletableFuture<T> x = Mono.fromCallable(callable)
.subscribeOn(executor)
.timeout(timeout, fallback)
.toFuture();
lastRequest = x;
return x;
}
public synchronized <T> CompletableFuture<T> invoke(Callable<T> callable) {
CompletableFuture<T> x = Mono.fromCallable(callable).subscribeOn(executor).toFuture();
lastRequest = x;

View File

@@ -49,7 +49,7 @@ public class LanguageServerAutoConf {
Optional<CompletionFilter> completionFilter,
ApplicationContext appContext
) throws Exception {
SimpleLanguageServer server = new SimpleLanguageServer(props.getExtensionId(), appContext);
SimpleLanguageServer server = new SimpleLanguageServer(props.getExtensionId(), appContext, props);
server.setCompletionFilter(completionFilter);
severities.ifPresent(server::setDiagnosticSeverityProvider);
return server;

View File

@@ -15,6 +15,7 @@ import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.text.IDocument;
import org.springframework.ide.vscode.commons.util.text.IRegion;
@@ -36,7 +37,7 @@ public class DocumentEditsTest {
@Before
public void setup() throws Exception {
SimpleLanguageServer server = new SimpleLanguageServer("dont-care", null);
SimpleLanguageServer server = new SimpleLanguageServer("dont-care", null, new LanguageServerProperties());
harness = new LanguageServerHarness(server, LanguageId.PLAINTEXT);
}

View File

@@ -27,6 +27,7 @@ import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents
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.composable.CompositeLanguageServerComponents;
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.HoverHandler;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -61,12 +62,14 @@ public class BootLanguageServerInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BootLanguageServerInitializer.class);
private static ProjectObserver.Listener reconcileOpenDocuments(SimpleLanguageServer s, CompositeLanguageServerComponents c) {
private static ProjectObserver.Listener reconcileOpenDocuments(SimpleLanguageServer s, CompositeLanguageServerComponents c, JavaProjectFinder projectFinder) {
return ProjectObserver.onAny(project -> {
c.getReconcileEngine().ifPresent(reconciler -> {
log.info("A project changed {}, triggering reconcile on all open documents", project.getElementName());
log.info("A project changed {}, triggering reconcile on all project's open documents", project.getElementName());
for (TextDocument doc : s.getTextDocumentService().getAll()) {
s.validateWith(doc.getId(), reconciler);
if (projectFinder.find(doc.getId()).orElse(null) == project) {
s.validateWith(doc.getId(), reconciler);
}
}
});
});
@@ -81,7 +84,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
builder.add(new BootJavaLanguageServerComponents(server, params, sourceLinks, cuCache, adHocProperties, symbolCache, config, springIndexer, runningAppProvider));
builder.add(new SpringXMLLanguageServerComponents(server, springIndexer, params, config));
components = builder.build(server);
params.projectObserver.addListener(reconcileOpenDocuments(server, components));
params.projectObserver.addListener(reconcileOpenDocuments(server, components, params.projectFinder));
SimpleTextDocumentService documents = server.getTextDocumentService();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff