Async handling for lsp workspace notifications
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user