Re-work project reconcile for optimization purposes
This commit is contained in:
@@ -10,16 +10,9 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.springframework.ide.vscode.boot.app;
|
package org.springframework.ide.vscode.boot.app;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.util.ArrayList;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
@@ -27,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
||||||
|
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
|
||||||
import org.springframework.ide.vscode.boot.factories.SpringFactoriesLanguageServerComponents;
|
import org.springframework.ide.vscode.boot.factories.SpringFactoriesLanguageServerComponents;
|
||||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
||||||
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
import org.springframework.ide.vscode.boot.java.links.JavaElementLocationProvider;
|
||||||
@@ -39,16 +33,15 @@ import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexPro
|
|||||||
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
|
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
|
||||||
import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine;
|
import org.springframework.ide.vscode.boot.validation.BootVersionValidationEngine;
|
||||||
import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents;
|
import org.springframework.ide.vscode.boot.xml.SpringXMLLanguageServerComponents;
|
||||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.completion.CompositeCompletionEngine;
|
import org.springframework.ide.vscode.commons.languageserver.completion.CompositeCompletionEngine;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
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.completion.VscodeCompletionEngineAdapter;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.composable.CompositeLanguageServerComponents;
|
import org.springframework.ide.vscode.commons.languageserver.composable.CompositeLanguageServerComponents;
|
||||||
|
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
|
import org.springframework.ide.vscode.commons.languageserver.config.LanguageServerProperties;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
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.java.ProjectObserver;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||||
@@ -59,18 +52,9 @@ import org.springframework.ide.vscode.commons.yaml.structure.YamlStructureProvid
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import reactor.core.Disposable;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import reactor.core.scheduler.Scheduler;
|
|
||||||
import reactor.core.scheduler.Schedulers;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class BootLanguageServerInitializer implements InitializingBean {
|
public class BootLanguageServerInitializer implements InitializingBean {
|
||||||
|
|
||||||
private static final long DEBOUNCE_PERIOD_PROJECT_RECONCILE = 500;
|
|
||||||
|
|
||||||
private static final List<String> FILES_TO_WATCH_GLOB = List.of("**/*.java");
|
|
||||||
|
|
||||||
@Autowired SimpleLanguageServer server;
|
@Autowired SimpleLanguageServer server;
|
||||||
@Autowired BootLanguageServerParams params;
|
@Autowired BootLanguageServerParams params;
|
||||||
@Autowired SourceLinks sourceLinks;
|
@Autowired SourceLinks sourceLinks;
|
||||||
@@ -93,9 +77,6 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
|
|
||||||
private CompositeLanguageServerComponents components;
|
private CompositeLanguageServerComponents components;
|
||||||
private VscodeCompletionEngineAdapter completionEngineAdapter;
|
private VscodeCompletionEngineAdapter completionEngineAdapter;
|
||||||
private List<IJavaProjectReconcileEngine> projectReconcilers;
|
|
||||||
private Scheduler projectReconcileScheduler = Schedulers.newBoundedElastic(1, Integer.MAX_VALUE, "Project-Reconciler", 10);
|
|
||||||
private Map<URI, Disposable> projectReconcileRequests = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BootLanguageServerInitializer.class);
|
private static final Logger log = LoggerFactory.getLogger(BootLanguageServerInitializer.class);
|
||||||
|
|
||||||
@@ -104,7 +85,6 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleted(IJavaProject project) {
|
public void deleted(IJavaProject project) {
|
||||||
doNotValidateProject(project, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,8 +106,6 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
s.validateWith(doc.getId(), reconciler);
|
s.validateWith(doc.getId(), reconciler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateProject(project, reconciler);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -138,26 +116,39 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
//TODO: CompositeLanguageServerComponents object instance serves no purpose anymore. The constructor really just contains
|
//TODO: CompositeLanguageServerComponents object instance serves no purpose anymore. The constructor really just contains
|
||||||
// some server intialization code. Migrate that code and get rid of the ComposableLanguageServer class
|
// some server intialization code. Migrate that code and get rid of the ComposableLanguageServer class
|
||||||
CompositeLanguageServerComponents.Builder builder = new CompositeLanguageServerComponents.Builder();
|
CompositeLanguageServerComponents.Builder builder = new CompositeLanguageServerComponents.Builder();
|
||||||
builder.add(new BootPropertiesLanguageServerComponents(server, params, javaElementLocationProvider, parser, yamlStructureProvider, yamlAssistContextProvider, sourceLinks));
|
List<LanguageServerComponents> componentsList = List.of(
|
||||||
BootJavaLanguageServerComponents bootJavaLanguageServerComponent = new BootJavaLanguageServerComponents(appContext);
|
new BootPropertiesLanguageServerComponents(server, params, javaElementLocationProvider, parser, yamlStructureProvider, yamlAssistContextProvider, sourceLinks),
|
||||||
builder.add(bootJavaLanguageServerComponent);
|
new BootJavaLanguageServerComponents(appContext),
|
||||||
builder.add(new SpringXMLLanguageServerComponents(server, springIndexer, params, config));
|
new SpringXMLLanguageServerComponents(server, springIndexer, params, config),
|
||||||
builder.add(new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config));
|
new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config)
|
||||||
components = builder.build(server);
|
|
||||||
|
|
||||||
projectReconcilers = List.of(
|
|
||||||
(IJavaProjectReconcileEngine) bootJavaLanguageServerComponent.getReconcileEngine().get(),
|
|
||||||
new BootVersionValidationEngine(server, config)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
SimpleTextDocumentService documents = server.getTextDocumentService();
|
List<ProjectReconcileScheduler> reconcileSchedulers = new ArrayList<>(componentsList.size() + 1);
|
||||||
|
for (LanguageServerComponents c : componentsList) {
|
||||||
|
builder.add(c);
|
||||||
|
|
||||||
components.getReconcileEngine().ifPresent(reconcileEngine -> {
|
if (!configProps.isReconcileOnlyOpenedDocs()) {
|
||||||
documents.onDidChangeContent(params -> {
|
c.getReconcileEngine()
|
||||||
TextDocument doc = params.getDocument();
|
.filter(IJavaProjectReconcileEngine.class::isInstance)
|
||||||
server.validateWith(doc.getId(), reconcileEngine);
|
.map(IJavaProjectReconcileEngine.class::cast)
|
||||||
});
|
.map(r -> r.getScheduler())
|
||||||
});
|
.ifPresent(reconcileSchedulers::add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version reconciler is for Maven/Gradle build files which are not part of
|
||||||
|
// server components because they come from docs we don't support at the moment
|
||||||
|
reconcileSchedulers.add(new BootVersionValidationEngine(server, config, params.projectObserver, projectFinder).getScheduler());
|
||||||
|
|
||||||
|
// Kick off project reconcile schedulers
|
||||||
|
for (ProjectReconcileScheduler scheduler : reconcileSchedulers) {
|
||||||
|
scheduler.start();
|
||||||
|
server.onShutdown(() -> scheduler.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
components = builder.build(server);
|
||||||
|
|
||||||
|
SimpleTextDocumentService documents = server.getTextDocumentService();
|
||||||
|
|
||||||
if (!completionEngines.isEmpty()) {
|
if (!completionEngines.isEmpty()) {
|
||||||
CompositeCompletionEngine compositeCompletionEngine = new CompositeCompletionEngine();
|
CompositeCompletionEngine compositeCompletionEngine = new CompositeCompletionEngine();
|
||||||
@@ -175,6 +166,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
|
|
||||||
components.getDocumentSymbolProvider().ifPresent(documents::onDocumentSymbol);
|
components.getDocumentSymbolProvider().ifPresent(documents::onDocumentSymbol);
|
||||||
|
|
||||||
|
|
||||||
if (recipesRepo != null) {
|
if (recipesRepo != null) {
|
||||||
recipesRepo.onRecipesLoaded(v -> {
|
recipesRepo.onRecipesLoaded(v -> {
|
||||||
// Recipes will start loading only after config has been received. Therefore safe to start listening to config changes now
|
// Recipes will start loading only after config has been received. Therefore safe to start listening to config changes now
|
||||||
@@ -183,37 +175,34 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
reconcile();
|
reconcile();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Reconcile would occur as listeners will be receiving events
|
||||||
startListeningToPerformReconcile();
|
startListeningToPerformReconcile();
|
||||||
reconcile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.onShutdown(() -> {
|
|
||||||
for (IJavaProject p : projectFinder.all()) {
|
|
||||||
doNotValidateProject(p, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startListeningToPerformReconcile() {
|
private void startListeningToPerformReconcile() {
|
||||||
|
components.getReconcileEngine().ifPresent(reconcileEngine -> {
|
||||||
|
server.getTextDocumentService().onDidChangeContent(params -> {
|
||||||
|
TextDocument doc = params.getDocument();
|
||||||
|
server.validateWith(doc.getId(), reconcileEngine);
|
||||||
|
});
|
||||||
|
});
|
||||||
config.addListener(evt -> reconcile());
|
config.addListener(evt -> reconcile());
|
||||||
|
|
||||||
params.projectObserver.addListener(reconcileDocumentsForProjectChange(server, components, params.projectFinder));
|
params.projectObserver.addListener(reconcileDocumentsForProjectChange(server, components, params.projectFinder));
|
||||||
|
|
||||||
server.getWorkspaceService().getFileObserver().onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles);
|
// // TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects.
|
||||||
server.getWorkspaceService().getFileObserver().onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles);
|
// // Need to figure out a check if spring index has any changes
|
||||||
|
//// springIndexer.onUpdate(v -> reconcile());
|
||||||
// TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects.
|
|
||||||
// Need to figure out a check if spring index has any changes
|
|
||||||
// springIndexer.onUpdate(v -> reconcile());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconcile() {
|
private void reconcile() {
|
||||||
components.getReconcileEngine().ifPresent(reconciler -> {
|
components.getReconcileEngine().ifPresent(reconciler -> {
|
||||||
log.info("A configuration changed, triggering reconcile on all open documents");
|
log.info("Triggering reconcile on all open documents");
|
||||||
for (TextDocument doc : server.getTextDocumentService().getAll()) {
|
for (TextDocument doc : server.getTextDocumentService().getAll()) {
|
||||||
server.validateWith(doc.getId(), reconciler);
|
server.validateWith(doc.getId(), reconciler);
|
||||||
}
|
}
|
||||||
params.projectFinder.all().forEach(p -> validateProject(p, reconciler));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,79 +217,4 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateProject(IJavaProject project, IReconcileEngine reconcileEngine) {
|
|
||||||
if (configProps.isReconcileOnlyOpenedDocs()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
URI uri = project.getLocationUri();
|
|
||||||
|
|
||||||
Disposable previousRequest = projectReconcileRequests.put(uri, Mono.delay(Duration.ofMillis(DEBOUNCE_PERIOD_PROJECT_RECONCILE))
|
|
||||||
.publishOn(projectReconcileScheduler)
|
|
||||||
.doOnSuccess(l -> {
|
|
||||||
if (projectReconcileRequests.remove(uri) != null) {
|
|
||||||
projectFinder.find(new TextDocumentIdentifier(uri.toASCIIString())).ifPresent(p -> {
|
|
||||||
for (IJavaProjectReconcileEngine projectReconciler : projectReconcilers) {
|
|
||||||
projectReconciler.clear(project);
|
|
||||||
projectReconciler.reconcile(p, doc -> server.createProblemCollector(doc));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.subscribe());
|
|
||||||
// Dispose previous request to debounce project reconcile requests.
|
|
||||||
if (previousRequest != null) {
|
|
||||||
previousRequest.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doNotValidateProject(IJavaProject project, boolean asyncClear) {
|
|
||||||
if (configProps.isReconcileOnlyOpenedDocs()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
URI uri = project.getLocationUri();
|
|
||||||
Disposable request = projectReconcileRequests.remove(uri);
|
|
||||||
if (request != null) {
|
|
||||||
request.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: Look at LanguageServerHarness to fix the deadlock that occurs every 2 second time maven build is ran
|
|
||||||
* If #clear(IJavaProject) is synchronous then the locked LanguageServerHarness instance is attempted to call publishDiagnostic()
|
|
||||||
* which is caused by the #clear(...) call. In the LS reality this will never happen as #publishDiagnsotics() is always a future
|
|
||||||
*/
|
|
||||||
if (asyncClear) {
|
|
||||||
Mono.fromFuture(CompletableFuture.runAsync(() -> projectReconcilers.forEach(projectReconciler -> projectReconciler.clear(project))))
|
|
||||||
.publishOn(projectReconcileScheduler);
|
|
||||||
} else {
|
|
||||||
for (IJavaProjectReconcileEngine projectReconciler : projectReconcilers) {
|
|
||||||
projectReconciler.clear(project);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleFiles(String[] files) {
|
|
||||||
if (configProps.isReconcileOnlyOpenedDocs()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
components.getReconcileEngine().ifPresent(reconcileEngine -> {
|
|
||||||
for (String f : files) {
|
|
||||||
URI uri = URI.create(f);
|
|
||||||
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
|
|
||||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(docId.getUri());
|
|
||||||
if (doc == null) {
|
|
||||||
projectFinder.find(docId).ifPresent(project -> {
|
|
||||||
Path p = Paths.get(uri);
|
|
||||||
if (IClasspathUtil.getSourceFolders(project.getClasspath()).filter(folder -> p.startsWith(folder.toPath())).findFirst().isPresent()) {
|
|
||||||
validateProject(project, reconcileEngine);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,14 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.springframework.ide.vscode.boot.common;
|
package org.springframework.ide.vscode.boot.common;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
|
||||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
|
||||||
|
|
||||||
public interface IJavaProjectReconcileEngine {
|
public interface IJavaProjectReconcileEngine {
|
||||||
|
|
||||||
void reconcile(IJavaProject project, Function<TextDocument, IProblemCollector> problemCollectorFactory);
|
void reconcile(IJavaProject project);
|
||||||
|
|
||||||
void clear(IJavaProject project);
|
void clear(IJavaProject project);
|
||||||
|
|
||||||
|
ProjectReconcileScheduler getScheduler();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2023 VMware, 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
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* VMware, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.ide.vscode.boot.common;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
|
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||||
|
|
||||||
|
import reactor.core.Disposable;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
public abstract class ProjectReconcileScheduler {
|
||||||
|
|
||||||
|
private IJavaProjectReconcileEngine reconciler;
|
||||||
|
private JavaProjectFinder projectFinder;
|
||||||
|
private long debounce;
|
||||||
|
|
||||||
|
private Scheduler projectReconcileScheduler;
|
||||||
|
private Map<URI, Disposable> projectReconcileRequests = new ConcurrentHashMap<>();
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
|
public ProjectReconcileScheduler(IJavaProjectReconcileEngine reconciler, JavaProjectFinder projectFinder, long debounce, int numberOFthreads) {
|
||||||
|
this.reconciler = reconciler;
|
||||||
|
this.debounce = debounce;
|
||||||
|
this.projectFinder = projectFinder;
|
||||||
|
|
||||||
|
projectReconcileScheduler = Schedulers.newBoundedElastic(numberOFthreads, Integer.MAX_VALUE, "Project-Reconciler", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectReconcileScheduler(IJavaProjectReconcileEngine reconciler, JavaProjectFinder projectFinder) {
|
||||||
|
this(reconciler, projectFinder, 500, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final synchronized void start() {
|
||||||
|
if (!started) {
|
||||||
|
init();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final synchronized void stop() {
|
||||||
|
if (started) {
|
||||||
|
dispose();
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dispose() {
|
||||||
|
for (IJavaProject p : projectFinder.all()) {
|
||||||
|
unscheduleValidation(p);
|
||||||
|
reconciler.clear(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void scheduleValidationForAllProjects() {
|
||||||
|
for (IJavaProject p : projectFinder.all()) {
|
||||||
|
scheduleValidation(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void scheduleValidation(IJavaProject project) {
|
||||||
|
URI uri = project.getLocationUri();
|
||||||
|
|
||||||
|
Disposable previousRequest = projectReconcileRequests.put(uri, Mono.delay(Duration.ofMillis(debounce))
|
||||||
|
.publishOn(projectReconcileScheduler)
|
||||||
|
.doOnSuccess(l -> {
|
||||||
|
if (projectReconcileRequests.remove(uri) != null) {
|
||||||
|
projectFinder.find(new TextDocumentIdentifier(uri.toASCIIString())).ifPresent(p -> {
|
||||||
|
reconciler.clear(project);
|
||||||
|
reconciler.reconcile(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.subscribe());
|
||||||
|
// Dispose previous request to debounce project reconcile requests.
|
||||||
|
if (previousRequest != null) {
|
||||||
|
previousRequest.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void unscheduleValidation(IJavaProject project) {
|
||||||
|
URI uri = project.getLocationUri();
|
||||||
|
Disposable request = projectReconcileRequests.remove(uri);
|
||||||
|
if (request != null) {
|
||||||
|
request.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void clear(IJavaProject project, boolean async) {
|
||||||
|
/*
|
||||||
|
* TODO: Look at LanguageServerHarness to fix the deadlock that occurs every 2 second time maven build is ran
|
||||||
|
* If #clear(IJavaProject) is synchronous then the locked LanguageServerHarness instance is attempted to call publishDiagnostic()
|
||||||
|
* which is caused by the #clear(...) call. In the LS reality this will never happen as #publishDiagnsotics() is always a future
|
||||||
|
*/
|
||||||
|
if (async) {
|
||||||
|
Mono.fromRunnable(() -> reconciler.clear(project))
|
||||||
|
.publishOn(projectReconcileScheduler)
|
||||||
|
.subscribe();
|
||||||
|
} else {
|
||||||
|
reconciler.clear(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JavaProjectFinder getProjectFinder() {
|
||||||
|
return projectFinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -191,8 +191,10 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
|||||||
|
|
||||||
RewriteCompilationUnitCache orCompilationUnitCache = appContext.getBean(RewriteCompilationUnitCache.class);
|
RewriteCompilationUnitCache orCompilationUnitCache = appContext.getBean(RewriteCompilationUnitCache.class);
|
||||||
|
|
||||||
|
RewriteRecipeRepository recipeRepo = appContext.getBean(RewriteRecipeRepository.class);
|
||||||
|
|
||||||
RewriteReconciler rewriteJavaReconciler = new RewriteReconciler(
|
RewriteReconciler rewriteJavaReconciler = new RewriteReconciler(
|
||||||
appContext.getBean(RewriteRecipeRepository.class),
|
recipeRepo,
|
||||||
orCompilationUnitCache,
|
orCompilationUnitCache,
|
||||||
server.getQuickfixRegistry(),
|
server.getQuickfixRegistry(),
|
||||||
config
|
config
|
||||||
@@ -201,7 +203,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
|||||||
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
|
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
|
||||||
jdtReconciler,
|
jdtReconciler,
|
||||||
rewriteJavaReconciler
|
rewriteJavaReconciler
|
||||||
}, documents);
|
}, server, config, projectObserver, recipeRepo);
|
||||||
|
|
||||||
codeActionProvider = new BootJavaCodeActionProvider(
|
codeActionProvider = new BootJavaCodeActionProvider(
|
||||||
projectFinder,
|
projectFinder,
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2023 VMware, 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
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* VMware, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.ide.vscode.boot.java.handlers;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
|
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||||
|
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
||||||
|
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
|
||||||
|
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
|
||||||
|
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||||
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
|
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.SimpleTextDocumentService;
|
||||||
|
import org.springframework.ide.vscode.commons.util.FileObserver;
|
||||||
|
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||||
|
|
||||||
|
public class BootJavaProjectReconcilerScheduler extends ProjectReconcileScheduler {
|
||||||
|
|
||||||
|
private static final List<String> FILES_TO_WATCH_GLOB = List.of("**/*.java");
|
||||||
|
|
||||||
|
private ProjectObserver projectObserver;
|
||||||
|
private BootJavaConfig config;
|
||||||
|
private RewriteRecipeRepository recipesRepo;
|
||||||
|
private FileObserver fileObserver;
|
||||||
|
private SimpleTextDocumentService documents;
|
||||||
|
|
||||||
|
public BootJavaProjectReconcilerScheduler(IJavaProjectReconcileEngine reconciler, FileObserver fileObserver, ProjectObserver projectObserver,
|
||||||
|
BootJavaConfig config, RewriteRecipeRepository recipesRepo, SimpleTextDocumentService documents, JavaProjectFinder projectFinder) {
|
||||||
|
super(reconciler, projectFinder);
|
||||||
|
this.fileObserver = fileObserver;
|
||||||
|
this.projectObserver = projectObserver;
|
||||||
|
this.config = config;
|
||||||
|
this.recipesRepo = recipesRepo;
|
||||||
|
this.documents = documents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
if (recipesRepo != null) {
|
||||||
|
recipesRepo.onRecipesLoaded(v -> {
|
||||||
|
// Recipes will start loading only after config has been received. Therefore safe to start listening to config changes now
|
||||||
|
// and launch initial project reconcile since both config and recipes are present
|
||||||
|
startListeningToPerformReconcile();
|
||||||
|
scheduleValidationForAllProjects();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
startListeningToPerformReconcile();
|
||||||
|
scheduleValidationForAllProjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startListeningToPerformReconcile() {
|
||||||
|
config.addListener(evt -> scheduleValidationForAllProjects());
|
||||||
|
|
||||||
|
projectObserver.addListener(new ProjectObserver.Listener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleted(IJavaProject project) {
|
||||||
|
unscheduleValidation(project);
|
||||||
|
clear(project, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void created(IJavaProject project) {
|
||||||
|
scheduleValidation(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changed(IJavaProject project) {
|
||||||
|
scheduleValidation(project);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fileObserver.onFilesChanged(FILES_TO_WATCH_GLOB, this::handleFiles);
|
||||||
|
fileObserver.onFilesCreated(FILES_TO_WATCH_GLOB, this::handleFiles);
|
||||||
|
|
||||||
|
// TODO: index update even happens on every file save. Very expensive to blindly reconcile all projects.
|
||||||
|
// Need to figure out a check if spring index has any changes
|
||||||
|
// springIndexer.onUpdate(v -> reconcile());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFiles(String[] files) {
|
||||||
|
for (String f : files) {
|
||||||
|
URI uri = URI.create(f);
|
||||||
|
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
|
||||||
|
TextDocument doc = documents.getLatestSnapshot(docId.getUri());
|
||||||
|
if (doc == null) {
|
||||||
|
getProjectFinder().find(docId).ifPresent(project -> {
|
||||||
|
Path p = Paths.get(uri);
|
||||||
|
if (IClasspathUtil.getSourceFolders(project.getClasspath())
|
||||||
|
.filter(folder -> p.startsWith(folder.toPath())).findFirst().isPresent()) {
|
||||||
|
scheduleValidation(project);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,22 +18,26 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||||
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
||||||
|
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
|
||||||
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
|
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
|
||||||
|
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRecipeRepository;
|
||||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
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.reconcile.IProblemCollector;
|
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||||
|
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||||
import org.springframework.ide.vscode.commons.util.text.LazyTextDocument;
|
import org.springframework.ide.vscode.commons.util.text.LazyTextDocument;
|
||||||
@@ -46,14 +50,26 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe
|
|||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BootJavaReconcileEngine.class);
|
private static final Logger log = LoggerFactory.getLogger(BootJavaReconcileEngine.class);
|
||||||
|
|
||||||
private final SimpleTextDocumentService documents;
|
|
||||||
private final JavaProjectFinder projectFinder;
|
private final JavaProjectFinder projectFinder;
|
||||||
private JavaReconciler[] javaReconcilers;
|
private final JavaReconciler[] javaReconcilers;
|
||||||
|
private final BootJavaProjectReconcilerScheduler projectReconeilerScheduler;
|
||||||
|
private final SimpleLanguageServer server;
|
||||||
|
|
||||||
public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers, SimpleTextDocumentService documents) {
|
public BootJavaReconcileEngine(JavaProjectFinder projectFinder, JavaReconciler[] javaReconcilers,
|
||||||
this.documents = documents;
|
SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver,
|
||||||
|
RewriteRecipeRepository recipeRepo) {
|
||||||
this.projectFinder = projectFinder;
|
this.projectFinder = projectFinder;
|
||||||
this.javaReconcilers = javaReconcilers;
|
this.javaReconcilers = javaReconcilers;
|
||||||
|
this.server = server;
|
||||||
|
this.projectReconeilerScheduler = new BootJavaProjectReconcilerScheduler(
|
||||||
|
this,
|
||||||
|
server.getWorkspaceService().getFileObserver(),
|
||||||
|
projectObserver,
|
||||||
|
config,
|
||||||
|
recipeRepo,
|
||||||
|
server.getTextDocumentService(),
|
||||||
|
projectFinder
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -113,7 +129,7 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reconcile(IJavaProject project, Function<TextDocument, IProblemCollector> problemCollectorFactory) {
|
public void reconcile(IJavaProject project) {
|
||||||
Stream<Path> files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> {
|
Stream<Path> files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> {
|
||||||
try {
|
try {
|
||||||
return Files.walk(folder.toPath()).filter(Files::isRegularFile);
|
return Files.walk(folder.toPath()).filter(Files::isRegularFile);
|
||||||
@@ -126,18 +142,17 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe
|
|||||||
.filter(f -> f.getFileName().toString().endsWith(".java"))
|
.filter(f -> f.getFileName().toString().endsWith(".java"))
|
||||||
.map(f -> new TextDocumentIdentifier(f.toUri().toASCIIString()));
|
.map(f -> new TextDocumentIdentifier(f.toUri().toASCIIString()));
|
||||||
|
|
||||||
List<TextDocument> docs = docIds.filter(docId -> documents.getLatestSnapshot(docId.getUri()) == null)
|
List<TextDocument> docs = docIds.filter(docId -> server.getTextDocumentService().getLatestSnapshot(docId.getUri()) == null)
|
||||||
.map(docId -> new LazyTextDocument(docId.getUri(), LanguageId.JAVA)).collect(Collectors.toList());
|
.map(docId -> new LazyTextDocument(docId.getUri(), LanguageId.JAVA)).collect(Collectors.toList());
|
||||||
|
|
||||||
Map<IDocument, IProblemCollector> problemCollectors = docs.stream()
|
Map<IDocument, IProblemCollector> problemCollectors = docs.stream()
|
||||||
.collect(Collectors.toMap(d -> d, d -> problemCollectorFactory.apply(d)));
|
.collect(Collectors.toMap(d -> d, d -> server.createProblemCollector(d)));
|
||||||
|
|
||||||
problemCollectors.values().forEach(c -> c.beginCollecting());
|
problemCollectors.values().forEach(c -> c.beginCollecting());
|
||||||
|
|
||||||
for (JavaReconciler jr : javaReconcilers) {
|
for (JavaReconciler jr : javaReconcilers) {
|
||||||
try {
|
try {
|
||||||
Map<IDocument, Collection<ReconcileProblem>> problems = jr.reconcile(project, docs,
|
Map<IDocument, Collection<ReconcileProblem>> problems = jr.reconcile(project, docs);
|
||||||
problemCollectorFactory);
|
|
||||||
problems.entrySet().forEach(e -> {
|
problems.entrySet().forEach(e -> {
|
||||||
IProblemCollector collector = problemCollectors.get(e.getKey());
|
IProblemCollector collector = problemCollectors.get(e.getKey());
|
||||||
e.getValue().forEach(p -> collector.accept(p));
|
e.getValue().forEach(p -> collector.accept(p));
|
||||||
@@ -161,8 +176,14 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(f -> f.getFileName().toString().endsWith(".java"))
|
.filter(f -> f.getFileName().toString().endsWith(".java"))
|
||||||
.forEach(p -> documents.publishDiagnostics(new TextDocumentIdentifier(p.toUri().toASCIIString()), Collections.emptyList()));
|
.filter(f -> server.getTextDocumentService().getLatestSnapshot(UriUtil.toUri(f.toFile()).toASCIIString()) == null)
|
||||||
|
.forEach(p -> server.getTextDocumentService().publishDiagnostics(new TextDocumentIdentifier(p.toUri().toASCIIString()), Collections.emptyList()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProjectReconcileScheduler getScheduler() {
|
||||||
|
return projectReconeilerScheduler;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2022 VMware, Inc.
|
* Copyright (c) 2022, 2023 VMware, Inc.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
@@ -13,7 +13,6 @@ package org.springframework.ide.vscode.boot.java.reconcilers;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||||
@@ -25,7 +24,6 @@ public interface JavaReconciler {
|
|||||||
|
|
||||||
void reconcile(IJavaProject project, IDocument doc, IProblemCollector problemCollector);
|
void reconcile(IJavaProject project, IDocument doc, IProblemCollector problemCollector);
|
||||||
|
|
||||||
Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs,
|
Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs);
|
||||||
Function<TextDocument, IProblemCollector> problemCollectorFactory);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||||
import org.eclipse.jdt.core.dom.Annotation;
|
import org.eclipse.jdt.core.dom.Annotation;
|
||||||
@@ -160,8 +159,7 @@ public class JdtReconciler implements JavaReconciler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs,
|
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs) {
|
||||||
Function<TextDocument, IProblemCollector> problemCollectorFactory) {
|
|
||||||
|
|
||||||
if (config.isRewriteReconcileEnabled()) {
|
if (config.isRewriteReconcileEnabled()) {
|
||||||
// long start = System.currentTimeMillis();
|
// long start = System.currentTimeMillis();
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.openrewrite.ExecutionContext;
|
import org.openrewrite.ExecutionContext;
|
||||||
@@ -139,8 +138,7 @@ public class RewriteReconciler implements JavaReconciler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs,
|
public Map<IDocument, Collection<ReconcileProblem>> reconcile(IJavaProject project, List<TextDocument> docs) {
|
||||||
Function<TextDocument, IProblemCollector> problemCollectorFactory) {
|
|
||||||
|
|
||||||
if (!config.isRewriteReconcileEnabled()) {
|
if (!config.isRewriteReconcileEnabled()) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
@@ -162,9 +160,9 @@ public class RewriteReconciler implements JavaReconciler {
|
|||||||
}
|
}
|
||||||
JavaParser javaParser = ORAstUtils.createJavaParser(project);
|
JavaParser javaParser = ORAstUtils.createJavaParser(project);
|
||||||
javaParser.setSourceSet(MavenProjectParser.MAIN);
|
javaParser.setSourceSet(MavenProjectParser.MAIN);
|
||||||
allProblems.putAll(doReconcile(project, mainSources, problemCollectorFactory, javaParser));
|
allProblems.putAll(doReconcile(project, mainSources, javaParser));
|
||||||
javaParser.setSourceSet(MavenProjectParser.TEST);
|
javaParser.setSourceSet(MavenProjectParser.TEST);
|
||||||
allProblems.putAll(doReconcile(project, testSources, problemCollectorFactory, javaParser));
|
allProblems.putAll(doReconcile(project, testSources, javaParser));
|
||||||
|
|
||||||
long end = System.currentTimeMillis();
|
long end = System.currentTimeMillis();
|
||||||
log.info("reconciling project (OpenRewrite): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms");
|
log.info("reconciling project (OpenRewrite): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms");
|
||||||
@@ -244,8 +242,7 @@ public class RewriteReconciler implements JavaReconciler {
|
|||||||
private static final int BATCH = 50;
|
private static final int BATCH = 50;
|
||||||
|
|
||||||
// Parse in batches and share the parser
|
// Parse in batches and share the parser
|
||||||
private Map<IDocument, Collection<ReconcileProblem>> doReconcile(IJavaProject project, List<TextDocument> docs,
|
private Map<IDocument, Collection<ReconcileProblem>> doReconcile(IJavaProject project, List<TextDocument> docs, JavaParser javaParser) {
|
||||||
Function<TextDocument, IProblemCollector> problemCollectorFactory, JavaParser javaParser) {
|
|
||||||
Map<IDocument, Collection<ReconcileProblem>> allProblems = new HashMap<>();
|
Map<IDocument, Collection<ReconcileProblem>> allProblems = new HashMap<>();
|
||||||
if (javaParser != null && config.isRewriteReconcileEnabled()) {
|
if (javaParser != null && config.isRewriteReconcileEnabled()) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ public class SpringBootUpgrade {
|
|||||||
|
|
||||||
if (version.getMajor() == targetVersion.getMajor() && version.getMinor() == targetVersion.getMinor()) {
|
if (version.getMajor() == targetVersion.getMajor() && version.getMinor() == targetVersion.getMinor()) {
|
||||||
// patch version upgrade - treat as pom versions only upgrade
|
// patch version upgrade - treat as pom versions only upgrade
|
||||||
recipe.doNext(new UpgradeDependencyVersion("org.springframework.boot", "*", version.getMajor() + "." + version.getMinor() + ".x", null, null));
|
recipe.doNext(new UpgradeDependencyVersion("org.springframework.boot", "*", version.getMajor() + "." + version.getMinor() + ".x", null, null, null));
|
||||||
recipe.doNext(new UpgradeParentVersion("org.springframework.boot", "spring-boot-starter-parent", version.getMajor() + "." + version.getMinor() + ".x", null));
|
recipe.doNext(new UpgradeParentVersion("org.springframework.boot", "spring-boot-starter-parent", version.getMajor() + "." + version.getMinor() + ".x", null, null));
|
||||||
} else {
|
} else {
|
||||||
createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor()).stream()
|
createRecipeIdsChain(version.getMajor(), version.getMinor(), targetVersion.getMajor(), targetVersion.getMinor()).stream()
|
||||||
.map(recipeRepo::getRecipe)
|
.map(recipeRepo::getRecipe)
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
package org.springframework.ide.vscode.boot.validation;
|
package org.springframework.ide.vscode.boot.validation;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||||
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine;
|
||||||
|
import org.springframework.ide.vscode.boot.common.ProjectReconcileScheduler;
|
||||||
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider;
|
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider;
|
||||||
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult;
|
import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult;
|
||||||
import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider;
|
import org.springframework.ide.vscode.boot.validation.generations.SpringIoProjectsProvider;
|
||||||
@@ -26,9 +26,9 @@ import org.springframework.ide.vscode.boot.validation.generations.SpringProjects
|
|||||||
import org.springframework.ide.vscode.boot.validation.generations.VersionValidators;
|
import org.springframework.ide.vscode.boot.validation.generations.VersionValidators;
|
||||||
import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationPreferences;
|
import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationPreferences;
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
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.SimpleLanguageServer;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
|
||||||
|
|
||||||
public class BootVersionValidationEngine implements IJavaProjectReconcileEngine {
|
public class BootVersionValidationEngine implements IJavaProjectReconcileEngine {
|
||||||
|
|
||||||
@@ -36,13 +36,41 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine
|
|||||||
|
|
||||||
private SimpleLanguageServer server;
|
private SimpleLanguageServer server;
|
||||||
private BootJavaConfig config;
|
private BootJavaConfig config;
|
||||||
|
private ProjectReconcileScheduler projectReconcileScheduler;
|
||||||
|
|
||||||
public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config) {
|
public BootVersionValidationEngine(SimpleLanguageServer server, BootJavaConfig config, ProjectObserver projectObserver, JavaProjectFinder projectFinder) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.projectReconcileScheduler = new ProjectReconcileScheduler(this, projectFinder) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
config.addListener(evt -> scheduleValidationForAllProjects());
|
||||||
|
projectObserver.addListener(new ProjectObserver.Listener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleted(IJavaProject project) {
|
||||||
|
unscheduleValidation(project);
|
||||||
|
clear(project, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void created(IJavaProject project) {
|
||||||
|
scheduleValidation(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changed(IJavaProject project) {
|
||||||
|
scheduleValidation(project);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reconcile(IJavaProject project, Function<TextDocument, IProblemCollector> problemCollectorFactory) {
|
public void reconcile(IJavaProject project) {
|
||||||
if (config.isBootVersionValidationEnabled()) {
|
if (config.isBootVersionValidationEnabled()) {
|
||||||
log.debug("validating Spring Boot version on project: " + project.getElementName());
|
log.debug("validating Spring Boot version on project: " + project.getElementName());
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
@@ -86,4 +114,9 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine
|
|||||||
Collections.emptyList());
|
Collections.emptyList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProjectReconcileScheduler getScheduler() {
|
||||||
|
return projectReconcileScheduler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ public class ValueSpelExpressionValidationTest {
|
|||||||
@Autowired private CompilationUnitCache compilationUnitCache;
|
@Autowired private CompilationUnitCache compilationUnitCache;
|
||||||
@Autowired private SimpleLanguageServer server;
|
@Autowired private SimpleLanguageServer server;
|
||||||
@Autowired private BootJavaConfig config;
|
@Autowired private BootJavaConfig config;
|
||||||
|
@Autowired private ProjectObserver projectObserver;
|
||||||
|
|
||||||
private File directory;
|
private File directory;
|
||||||
private String docUri;
|
private String docUri;
|
||||||
@@ -149,7 +150,7 @@ public class ValueSpelExpressionValidationTest {
|
|||||||
problemCollector = new TestProblemCollector();
|
problemCollector = new TestProblemCollector();
|
||||||
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
|
reconcileEngine = new BootJavaReconcileEngine(projectFinder, new JavaReconciler[] {
|
||||||
new JdtReconciler(compilationUnitCache, config)
|
new JdtReconciler(compilationUnitCache, config)
|
||||||
}, server.getTextDocumentService());
|
}, server, config, projectObserver, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
|||||||
Reference in New Issue
Block a user