Async handling for lsp workspace notifications

This commit is contained in:
aboyko
2023-08-30 10:09:39 -04:00
parent 1377be2ef5
commit c5ae0485b9
8 changed files with 68 additions and 61 deletions

View File

@@ -18,12 +18,14 @@ package org.springframework.ide.vscode.commons.languageserver;
*/
public abstract class AbstractProgressTask {
private static long progress_counter = 0;
protected final String taskId;
protected final ProgressService service;
public AbstractProgressTask(String taskId, ProgressService service) {
this.taskId = taskId;
this.taskId = taskId + "-" + (progress_counter++);
this.service = service;
}

View File

@@ -38,6 +38,7 @@ import org.eclipse.lsp4j.services.WorkspaceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.AsyncRunner;
import org.springframework.ide.vscode.commons.util.FileObserver;
import com.google.common.collect.ImmutableList;
@@ -55,6 +56,7 @@ public class SimpleWorkspaceService implements WorkspaceService {
private ExecuteCommandHandler executeCommandHandler;
private WorkspaceSymbolHandler workspaceSymbolHandler;
private SimpleServerFileObserver fileObserver;
private AsyncRunner asyncRunner;
private ListenerList<DidChangeWorkspaceFoldersParams> workspaceFolderListeners = new ListenerList<>();
@@ -62,12 +64,13 @@ public class SimpleWorkspaceService implements WorkspaceService {
public SimpleWorkspaceService(SimpleLanguageServer server) {
this.fileObserver = new SimpleServerFileObserver(server);
this.messageWorkerThreadPool = Executors.newCachedThreadPool();
this.asyncRunner = server.getAsync();
}
@Override
public CompletableFuture<Either<List<? extends SymbolInformation>, List<? extends WorkspaceSymbol>>> symbol(WorkspaceSymbolParams params) {
log.info("request for workspace symbols arrived: " + params.getQuery());
return CompletableFutures.computeAsync(messageWorkerThreadPool, cancelToken -> {
WorkspaceSymbolHandler workspaceSymbolHandler = this.workspaceSymbolHandler;
@@ -86,36 +89,38 @@ public class SimpleWorkspaceService implements WorkspaceService {
@Override
public void didChangeConfiguration(DidChangeConfigurationParams params) {
configurationListeners.fire(new Settings((JsonElement) params.getSettings()));
asyncRunner.execute(() -> configurationListeners.fire(new Settings((JsonElement) params.getSettings())));
}
@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
try {
Map<FileChangeType, List<FileEvent>> collect =
params.getChanges().stream().filter(event -> event.getUri() != null).collect(Collectors.groupingBy(FileEvent::getType));
for (FileChangeType type : collect.keySet()) {
String[] docURIs = collect.get(type).stream().map(event -> event.getUri()).toArray(String[]::new);
switch (type) {
case Created:
fileObserver.notifyFilesCreated(docURIs);
break;
case Changed:
fileObserver.notifyFilesChanged(docURIs);
break;
case Deleted:
fileObserver.notifyFilesDeleted(docURIs);
break;
default:
log.warn("Uknown file change type '" + type + "' for files: " + docURIs);
break;
asyncRunner.execute(() -> {
try {
Map<FileChangeType, List<FileEvent>> collect =
params.getChanges().stream().filter(event -> event.getUri() != null).collect(Collectors.groupingBy(FileEvent::getType));
for (FileChangeType type : collect.keySet()) {
String[] docURIs = collect.get(type).stream().map(event -> event.getUri()).toArray(String[]::new);
switch (type) {
case Created:
fileObserver.notifyFilesCreated(docURIs);
break;
case Changed:
fileObserver.notifyFilesChanged(docURIs);
break;
case Deleted:
fileObserver.notifyFilesDeleted(docURIs);
break;
default:
log.warn("Uknown file change type '" + type + "' for files: " + docURIs);
break;
}
}
} catch (Throwable t) {
log.warn("problem occurred while dispatching file event", t);
}
} catch (Throwable t) {
log.warn("problem occurred while dispatching file event", t);
}
});
}
@Override
@@ -131,7 +136,7 @@ public class SimpleWorkspaceService implements WorkspaceService {
changed = true;
}
if (changed) {
workspaceFolderListeners.fire(params);
asyncRunner.execute(() -> workspaceFolderListeners.fire(params));
}
}

View File

@@ -498,6 +498,7 @@ public class LanguageServerHarness {
public synchronized void changeFile(String uri) {
FileEvent fileEvent = new FileEvent(uri, FileChangeType.Changed);
getServer().getWorkspaceService().didChangeWatchedFiles(new DidChangeWatchedFilesParams(Arrays.asList(fileEvent)));
getServer().getAsync().waitForAll();
}
public synchronized void createFile(String uri) {
@@ -959,6 +960,7 @@ public class LanguageServerHarness {
public void changeConfiguration(Settings settings) {
getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(settings.getRawSettings()));
getServer().getAsync().waitForAll();
}
public SimpleLanguageServer getServer() {

View File

@@ -23,7 +23,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.junit.jupiter.api.Test;
@@ -31,6 +30,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFClientParams;
import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.ClientParamsProvider;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
import org.springframework.ide.vscode.manifest.yaml.bootiful.ManifestLanguageServerTest;
@@ -108,12 +108,7 @@ public class ManifestYamlLanguageServerInitializerTest {
assertEquals(Arrays.asList("test.io"), serverInitializer.getCfTargets());
// This tests a change in workspace (e.g. boot dash) that results in two more targets created.
DidChangeConfigurationParams params = new DidChangeConfigurationParams();
JsonParser parser = new JsonParser();
params.setSettings(parser.parse(new InputStreamReader(getClass().getResourceAsStream("/cf-targets1.json"))));
server.getWorkspaceService().didChangeConfiguration(params);
harness.changeConfiguration(new Settings(JsonParser.parseReader(new InputStreamReader(getClass().getResourceAsStream("/cf-targets1.json")))));
assertEquals(3, getAllParams(serverInitializer.getParamsProvider()).size());
// End result should have the initial target as well as the two additional targets obtained on workspace change

View File

@@ -504,7 +504,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
}
CompletableFuture<Void> future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
future = future.thenAcceptAsync(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
future = future.thenAccept(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
return future;
}
}
@@ -565,7 +565,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
}
}
CompletableFuture<Void> future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
future = future.thenAcceptAsync(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
future = future.thenAccept(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
return future;
}
}
@@ -640,7 +640,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
}
CompletableFuture<Void> future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
future = future.thenAcceptAsync(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
future = future.thenAccept(v -> server.getClient().indexUpdated()).thenAccept(v -> listeners.fire(v));
return future;
}
catch (Exception e) {

View File

@@ -80,21 +80,22 @@ public class SpringPropertiesIndexManager extends ListenerManager<Listener<Sprin
IndefiniteProgressTask progress = progressService == null ? null
: progressService.createIndefiniteProgressTask(getProgressId(), "Indexing Spring Boot Properties",
null);
try {
Builder builder = SpringPropertyIndex.builder(valueProviders).withClasspath(project.getClasspath());
if (commonPropertiesMetadata != null) {
builder.withMetadata(commonPropertiesMetadata.get());
}
SpringPropertyIndex index = builder.build();
Builder builder = SpringPropertyIndex.builder(valueProviders).withClasspath(project.getClasspath());
if (commonPropertiesMetadata != null) {
builder.withMetadata(commonPropertiesMetadata.get());
log.info("Indexing Spring Boot Properties for {} DONE", project.getElementName());
log.info("Indexed {} properties.", index.size());
return index;
} finally {
if (progress != null) {
progress.done();
}
}
SpringPropertyIndex index = builder.build();
if (progress != null) {
progress.done();
}
log.info("Indexing Spring Boot Properties for {} DONE", project.getElementName());
log.info("Indexed {} properties.", index.size());
return index;
}
public synchronized void clear() {

View File

@@ -18,7 +18,6 @@ import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Position;
@@ -27,8 +26,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -37,6 +34,7 @@ import org.springframework.ide.vscode.boot.app.BootLanguageServerBootApp;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.bootiful.XmlBeansTestConf;
import org.springframework.ide.vscode.boot.test.DefinitionLinkAsserts;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
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;
@@ -49,6 +47,9 @@ import org.springframework.test.annotation.DirtiesContext.ClassMode;
import com.google.gson.Gson;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
/**
* @author Alex Boyko
*/
@@ -92,7 +93,7 @@ public class XmlBeansHyperlinkTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
project = projects.mavenProject("test-xml-hyperlinks");
@@ -206,7 +207,7 @@ public class XmlBeansHyperlinkTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(DEFAULT_INDEX_WAIT_TIME, TimeUnit.SECONDS);
@@ -241,7 +242,8 @@ public class XmlBeansHyperlinkTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(DEFAULT_INDEX_WAIT_TIME, TimeUnit.SECONDS);
@@ -280,7 +282,7 @@ public class XmlBeansHyperlinkTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(DEFAULT_INDEX_WAIT_TIME, TimeUnit.SECONDS);
@@ -309,7 +311,7 @@ public class XmlBeansHyperlinkTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
CompletableFuture<Void> initProject = indexer.waitOperation();
initProject.get(DEFAULT_INDEX_WAIT_TIME, TimeUnit.SECONDS);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2021 Pivotal, Inc.
* Copyright (c) 2020, 2023 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
@@ -21,7 +21,6 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.TextDocumentIdentifier;
@@ -40,6 +39,7 @@ import org.springframework.ide.vscode.boot.xml.SpringXMLReconcileEngine;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
import org.springframework.ide.vscode.commons.util.text.LanguageId;
@@ -88,7 +88,7 @@ public class XMLSpelExpressionValidationTest {
Map<String, Object> settings = new HashMap<>();
settings.put("boot-java", bootJavaObj);
harness.getServer().getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(new Gson().toJsonTree(settings)));
harness.changeConfiguration(new Settings(new Gson().toJsonTree(settings)));
project = projects.mavenProject("test-xml-validations");
harness.useProject(project);