From 2f743fe9a8abfb25c50c9be60974bb347fd36b99 Mon Sep 17 00:00:00 2001 From: aboyko Date: Mon, 27 Feb 2023 17:43:20 -0500 Subject: [PATCH] Progress for project reconciling --- ...essTask.java => AbstractProgressTask.java} | 20 ++--- .../IndefiniteProgressTask.java | 41 +++++++++ .../PercentageProgressTask.java | 85 +++++++++++++++++++ .../languageserver/ProgressService.java | 58 +++++++++++-- .../util/SimpleLanguageServer.java | 14 +-- .../commons/rewrite/java/ORAstUtils.java | 16 +++- .../common/IJavaProjectReconcileEngine.java | 5 +- .../common/ProjectReconcileScheduler.java | 2 +- .../handlers/BootJavaReconcileEngine.java | 20 ++++- .../v2/SpringProcessConnectorService.java | 45 +++++----- .../boot/java/reconcilers/JavaReconciler.java | 6 +- .../boot/java/reconcilers/JdtReconciler.java | 2 +- .../rewrite/RewriteCompilationUnitCache.java | 4 +- .../boot/java/rewrite/RewriteReconciler.java | 19 +++-- .../java/rewrite/RewriteRefactorings.java | 3 +- .../BootVersionValidationEngine.java | 3 +- 16 files changed, 263 insertions(+), 80 deletions(-) rename headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/{ProgressTask.java => AbstractProgressTask.java} (64%) create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/IndefiniteProgressTask.java create mode 100644 headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/PercentageProgressTask.java diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressTask.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/AbstractProgressTask.java similarity index 64% rename from headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressTask.java rename to headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/AbstractProgressTask.java index ac15c3e55..8a397cba8 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressTask.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/AbstractProgressTask.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Pivotal, Inc. + * Copyright (c) 2019, 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 @@ -16,26 +16,18 @@ package org.springframework.ide.vscode.commons.languageserver; * This handler can be used for long-running progress that requires message updates to the same task. * */ -public class ProgressTask { +public abstract class AbstractProgressTask { - private final String taskId; - private final ProgressService service; + protected final String taskId; + protected final ProgressService service; - public ProgressTask(String taskId, ProgressService service) { + public AbstractProgressTask(String taskId, ProgressService service) { this.taskId = taskId; this.service = service; } - public void progressBegin(String title, String statusMsg) { - this.service.progressBegin(taskId, title, statusMsg); - } - - public void progressEvent(String statusMsg) { - this.service.progressEvent(taskId, statusMsg); - } - - public void progressDone() { + public void done() { this.service.progressDone(taskId); } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/IndefiniteProgressTask.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/IndefiniteProgressTask.java new file mode 100644 index 000000000..deb83587e --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/IndefiniteProgressTask.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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.commons.languageserver; + +import org.eclipse.lsp4j.WorkDoneProgressBegin; +import org.eclipse.lsp4j.WorkDoneProgressReport; + +public class IndefiniteProgressTask extends AbstractProgressTask { + + public IndefiniteProgressTask(String taskId, ProgressService service, String title, String message) { + super(taskId, service); + progressBegin(title, message); + } + + private void progressBegin(String title, String message) { + WorkDoneProgressBegin report = new WorkDoneProgressBegin(); + report.setTitle(title); + report.setCancellable(false); + if (message != null && !message.isEmpty()) { + report.setMessage(message); + } + service.progressBegin(taskId, report); + } + + public void progressEvent(String statusMsg) { + WorkDoneProgressReport report = new WorkDoneProgressReport(); + report.setMessage(statusMsg); + service.progressEvent(taskId, report); + } + + + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/PercentageProgressTask.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/PercentageProgressTask.java new file mode 100644 index 000000000..2e3dd0356 --- /dev/null +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/PercentageProgressTask.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * 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.commons.languageserver; + +import org.eclipse.lsp4j.WorkDoneProgressBegin; +import org.eclipse.lsp4j.WorkDoneProgressReport; +import org.springframework.ide.vscode.commons.languageserver.util.LspClient; + +/** + * Eclipse progress requires sending total work units number with the begin progress message. The number is any intger. + * VScode and likely other LSP clients require the begin progress message to have 0 and progress is assumed to be between 0 and 100. + * This class takes care of these differences. + * + * @author aboyko + * + */ +public class PercentageProgressTask extends AbstractProgressTask { + + private int total; + private int current; + + private int currentPercentage; + + public PercentageProgressTask(String taskId, ProgressService service, int total, String title) { + super(taskId, service); + this.total = total; + this.current = 0; + begin(title); + } + + private void begin(String title) { + WorkDoneProgressBegin progressBegin = new WorkDoneProgressBegin(); + progressBegin.setPercentage(LspClient.currentClient() == LspClient.Client.ECLIPSE ? 100 : 0); + progressBegin.setCancellable(false); + progressBegin.setTitle(title); + service.progressBegin(taskId, progressBegin); + } + + public int getTotal() { + return total; + } + + public int getCurrent() { + return current; + } + + public void setCurrent(int current) { + if (current > total) { + throw new IllegalArgumentException(); + } + this.current = current; + reportCurrent(); + } + + private void reportPercent(int percent) { + if (percent > currentPercentage) { + currentPercentage = percent; + WorkDoneProgressReport r = new WorkDoneProgressReport(); + r.setPercentage(percent); + service.progressEvent(taskId, r); + } + } + + private void reportCurrent() { + reportPercent(current * 100 / total); + } + + public void increment() { + if (current >= total) { + throw new IllegalStateException(); + } + current++; + reportCurrent(); + } + + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressService.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressService.java index 263d4c2f1..38a7ed18e 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressService.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/ProgressService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2022 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -11,16 +11,19 @@ package org.springframework.ide.vscode.commons.languageserver; +import org.eclipse.lsp4j.WorkDoneProgressBegin; +import org.eclipse.lsp4j.WorkDoneProgressReport; + public interface ProgressService { public static ProgressService NO_PROGRESS = new ProgressService() { @Override - public void progressBegin(String taskId, String title, String message) { + public void progressBegin(String taskId, WorkDoneProgressBegin report) { } @Override - public void progressEvent(String taskId, String statusMsg) { + public void progressEvent(String taskId, WorkDoneProgressReport report) { } @Override @@ -37,8 +40,28 @@ public interface ProgressService { * id for some kind of 'long running job' * @param title progress main title, i.e. "Indexing", "Loading" * @param message detail for the title, i.e. subtask in progress at the moment + * @deprecated Use {@link #progressBegin(String, WorkDoneProgressBegin)} */ - void progressBegin(String taskId, String title, String message); + default void progressBegin(String taskId, String title, String message) { + WorkDoneProgressBegin report = new WorkDoneProgressBegin(); + report.setCancellable(false); + if (message != null && !message.isEmpty()) { + report.setMessage(message); + } + report.setTitle(title); + + progressBegin(taskId, report); + } + + /** + * Sends an event to start progress to the LSP client. + * + * @param taskId is an arbitrary id + * that can be chosen by the caller. The purpose of the id is to be a 'unique' + * id for some kind of 'long running job' + * @param report the report either string messages or percentage + */ + void progressBegin(String taskId, WorkDoneProgressBegin report); /** * Sends a progress event to the LSP client. Each event updates the message shown @@ -48,8 +71,25 @@ public interface ProgressService { * * @param taskId * @param statusMsg + * + * @deprecated Use {@link #progressEvent(String, WorkDoneProgressReport)} */ - void progressEvent(String taskId, String statusMsg); + default void progressEvent(String taskId, String statusMsg) { + WorkDoneProgressReport report = new WorkDoneProgressReport(); + report.setMessage(statusMsg); + progressEvent(taskId, report); + } + + /** + * Sends a progress event to the LSP client. Each event updates the message shown + * to the user replacing the old one. + * More than one message may be shown simultaneously to the user, if they + * have different taskId. + * + * @param taskId + * @param report + */ + void progressEvent(String taskId, WorkDoneProgressReport report); /** * Send the event to the LSP client to end progress for passed id @@ -58,8 +98,12 @@ public interface ProgressService { */ void progressDone(String taskId); - default ProgressTask createProgressTask(String taskId) { - return new ProgressTask(taskId, this); + default IndefiniteProgressTask createIndefiniteProgressTask(String taskId, String title, String message) { + return new IndefiniteProgressTask(taskId, this, title, message); + } + + default PercentageProgressTask createPercentageProgressTask(String taskId, int totalWork, String title) { + return new PercentageProgressTask(taskId, this, totalWork, title); } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 15c3cfc0d..23d2a376a 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -149,8 +149,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC private ConcurrentHashMap activeTaskIDs = new ConcurrentHashMap<>(); - @Override - public void progressBegin(String taskId, String title, String message) { + public void progressBegin(String taskId, WorkDoneProgressBegin report) { STS4LanguageClient client = SimpleLanguageServer.this.client; if (client != null) { boolean isNew = activeTaskIDs.put(taskId, true) == null; @@ -162,18 +161,13 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC client.createProgress(params).thenAccept((p) -> { ProgressParams progressParams = new ProgressParams(); progressParams.setToken(taskId); - WorkDoneProgressBegin report = new WorkDoneProgressBegin(); - report.setCancellable(false); progressParams.setValue(Either.forLeft(report)); - report.setMessage(message); - report.setTitle(title); client.notifyProgress(progressParams); }); } } - - @Override - public void progressEvent(String taskId, String statusMsg) { + + public void progressEvent(String taskId, WorkDoneProgressReport report) { STS4LanguageClient client = SimpleLanguageServer.this.client; if (client != null) { if (!activeTaskIDs.containsKey(taskId)) { @@ -182,9 +176,7 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, LanguageC } ProgressParams progressParams = new ProgressParams(); progressParams.setToken(taskId); - WorkDoneProgressReport report = new WorkDoneProgressReport(); progressParams.setValue(Either.forLeft(report)); - report.setMessage(statusMsg); client.notifyProgress(progressParams); } } diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java index a8ee09413..90ee46056 100644 --- a/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java +++ b/headless-services/commons/commons-rewrite/src/main/java/org/springframework/ide/vscode/commons/rewrite/java/ORAstUtils.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -29,6 +30,7 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Parser; import org.openrewrite.Recipe; +import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.RecipeIntrospectionUtils; @@ -40,6 +42,7 @@ import org.openrewrite.java.UpdateSourcePositions; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.CompilationUnit; import org.openrewrite.marker.Range; +import org.openrewrite.tree.ParsingExecutionContextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.IClasspathUtil; @@ -245,7 +248,7 @@ public class ORAstUtils { } } - public static List parse(SimpleTextDocumentService documents, IJavaProject project) { + public static List parse(SimpleTextDocumentService documents, IJavaProject project, Consumer parseCallback) { List inputs = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { return Files.walk(folder.toPath()); @@ -269,7 +272,7 @@ public class ORAstUtils { } }).collect(Collectors.toList()); JavaParser javaParser = createJavaParser(project); - return ORAstUtils.parseInputs(javaParser, inputs); + return ORAstUtils.parseInputs(javaParser, inputs, parseCallback); } public static List parse(JavaParser parser, Iterable sourceFiles) { @@ -291,9 +294,14 @@ public class ORAstUtils { return finalCus; } - public static List parseInputs(JavaParser parser, Iterable inputs) { - InMemoryExecutionContext ctx = new InMemoryExecutionContext(ORAstUtils::logExceptionWhileParsing); + public static List parseInputs(JavaParser parser, Iterable inputs, Consumer parseCallback) { + ExecutionContext ctx = new InMemoryExecutionContext(ORAstUtils::logExceptionWhileParsing); ctx.putMessage(JavaParser.SKIP_SOURCE_SET_TYPE_GENERATION, true); + if (parseCallback != null) { + ParsingExecutionContextView parseContext = ParsingExecutionContextView.view(ctx); + parseContext.setParsingListener((input, source) -> parseCallback.accept(source)); + ctx = parseContext; + } List cus = Collections.emptyList(); long start = System.currentTimeMillis(); synchronized (parser) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/IJavaProjectReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/IJavaProjectReconcileEngine.java index 6472951a8..a82385707 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/IJavaProjectReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/IJavaProjectReconcileEngine.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 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 @@ -11,10 +11,11 @@ package org.springframework.ide.vscode.boot.common; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; public interface IJavaProjectReconcileEngine { - void reconcile(IJavaProject project); + void reconcile(IJavaProject project, ProgressService progressService); void clear(IJavaProject project); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/ProjectReconcileScheduler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/ProjectReconcileScheduler.java index 1d4c9441f..3d5a9f408 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/ProjectReconcileScheduler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/ProjectReconcileScheduler.java @@ -77,7 +77,7 @@ public abstract class ProjectReconcileScheduler { if (projectReconcileRequests.remove(uri) != null) { projectFinder.find(new TextDocumentIdentifier(uri.toASCIIString())).ifPresent(p -> { reconciler.clear(project); - reconciler.reconcile(p); + reconciler.reconcile(p, getServer().getProgressService()); }); } }) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java index 899f73f87..6a26e8569 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReconcileEngine.java @@ -28,6 +28,8 @@ import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler; import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.PercentageProgressTask; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; 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.IReconcileEngine; @@ -114,7 +116,7 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe } @Override - public void reconcile(IJavaProject project) { + public void reconcile(IJavaProject project, ProgressService progressService) { Stream files = IClasspathUtil.getProjectJavaSourceFolders(project.getClasspath()).flatMap(folder -> { try { return Files.walk(folder.toPath()).filter(Files::isRegularFile); @@ -134,10 +136,21 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe .collect(Collectors.toMap(d -> d, d -> server.createProblemCollector(d))); problemCollectors.values().forEach(c -> c.beginCollecting()); - + + int totalWork = 0; + for (JavaReconciler jr : javaReconcilers) { + totalWork += jr.getTotalWorkUnits(docs); + } + + PercentageProgressTask progressTask = progressService.createPercentageProgressTask( + "reconcile-java-" + project.getElementName(), + totalWork, + "Reconciling Spring Java for '" + project.getElementName() + "'" + ); + for (JavaReconciler jr : javaReconcilers) { try { - Map> problems = jr.reconcile(project, docs); + Map> problems = jr.reconcile(project, docs, () -> progressTask.increment()); problems.entrySet().forEach(e -> { IProblemCollector collector = problemCollectors.get(e.getKey()); e.getValue().forEach(p -> collector.accept(p)); @@ -147,6 +160,7 @@ public class BootJavaReconcileEngine implements IReconcileEngine, IJavaProjectRe } } + progressTask.done(); problemCollectors.values().forEach(c -> c.endCollecting()); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java index 4c190c0dd..874068c83 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Pivotal, Inc. + * Copyright (c) 2019, 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 @@ -19,8 +19,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.commons.languageserver.DiagnosticService; +import org.springframework.ide.vscode.commons.languageserver.IndefiniteProgressTask; import org.springframework.ide.vscode.commons.languageserver.ProgressService; -import org.springframework.ide.vscode.commons.languageserver.ProgressTask; import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageException; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; @@ -93,11 +93,8 @@ public class SpringProcessConnectorService { connector.addConnectorChangeListener(connectorListener); try { - final ProgressTask progressTask = getProgressTask( - "spring-process-connector-service-connect-" + processKey); - - progressTask.progressBegin("Connect", null); - + final IndefiniteProgressTask progressTask = getProgressTask( + "spring-process-connector-service-connect-" + processKey, "Connect", null); scheduleConnect(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0); } catch (Exception e) { @@ -110,10 +107,8 @@ public class SpringProcessConnectorService { SpringProcessConnector connector = this.connectors.get(springProcessParams.getProcessKey()); if (connector != null) { - final ProgressTask progressTask = getProgressTask( - "spring-process-connector-service-refresh-data-" + springProcessParams.getProcessKey()); - - progressTask.progressBegin("Refresh", null); + final IndefiniteProgressTask progressTask = getProgressTask( + "spring-process-connector-service-refresh-data-" + springProcessParams.getProcessKey(), "Refresh", null); scheduleRefresh(progressTask, springProcessParams, connector, 0, TimeUnit.SECONDS, 0); } @@ -140,11 +135,9 @@ public class SpringProcessConnectorService { this.connectedSuccess.put(processKey, false); if (connector != null) { - final ProgressTask progressTask = getProgressTask( - "spring-process-connector-service-disconnect-" + processKey); + final IndefiniteProgressTask progressTask = getProgressTask( + "spring-process-connector-service-disconnect-" + processKey, "Disconnect", null); - progressTask.progressBegin("Disconnect", null); - scheduleDisconnect(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0); } } @@ -165,7 +158,7 @@ public class SpringProcessConnectorService { return processID; } - private void scheduleConnect(ProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { + private void scheduleConnect(IndefiniteProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { String progressMessage = "Connecting to process: " + processKey + " - retry no: " + retryNo; log.info(progressMessage); @@ -174,7 +167,7 @@ public class SpringProcessConnectorService { try { progressTask.progressEvent(progressMessage); connector.connect(); - progressTask.progressDone(); + progressTask.done(); refreshProcess(new SpringProcessParams(processKey, "", "", "")); refreshProcess(new SpringProcessParams(processKey, METRICS, MEMORY, "")); @@ -186,7 +179,7 @@ public class SpringProcessConnectorService { if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) { scheduleConnect(progressTask, processKey, connector, retryDelayInSeconds, TimeUnit.SECONDS, retryNo + 1); } else { - progressTask.progressDone(); + progressTask.done(); // Send message to client if maximum retries reached on error if (isKnownProcessKey(processKey)) { @@ -198,7 +191,7 @@ public class SpringProcessConnectorService { }, delay, unit); } - private void scheduleDisconnect(ProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { + private void scheduleDisconnect(IndefiniteProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { String message = "Disconnect from process: " + processKey + " - retry no: " + retryNo; log.info(message); @@ -207,7 +200,7 @@ public class SpringProcessConnectorService { try { progressTask.progressEvent(message); connector.disconnect(); - progressTask.progressDone(); + progressTask.done(); } catch (Exception e) { log.info("problem occured during process disconnect", e); @@ -215,7 +208,7 @@ public class SpringProcessConnectorService { if (retryNo < maxRetryCount) { scheduleDisconnect(progressTask, processKey, connector, retryDelayInSeconds, TimeUnit.SECONDS, retryNo + 1); } else { - progressTask.progressDone(); + progressTask.done(); // Send message to client if maximum retries reached on error diagnosticService.diagnosticEvent(ShowMessageException @@ -226,7 +219,7 @@ public class SpringProcessConnectorService { }, delay, unit); } - private void scheduleRefresh(ProgressTask progressTask, SpringProcessParams springProcessParams, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { + private void scheduleRefresh(IndefiniteProgressTask progressTask, SpringProcessParams springProcessParams, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) { String processKey = springProcessParams.getProcessKey(); String endpoint = springProcessParams.getEndpoint(); String metricName = springProcessParams.getMetricName(); @@ -271,7 +264,7 @@ public class SpringProcessConnectorService { this.connectedSuccess.put(processKey, true); } } - progressTask.progressDone(); + progressTask.done(); } catch (Exception e) { @@ -282,7 +275,7 @@ public class SpringProcessConnectorService { retryNo + 1); } else { - progressTask.progressDone(); + progressTask.done(); // Send message to client if maximum retries reached on error if (isKnownProcessKey(processKey)) { @@ -298,7 +291,7 @@ public class SpringProcessConnectorService { }, delay, unit); } - private ProgressTask getProgressTask(String prefixId) { - return this.progressService.createProgressTask(prefixId + progressIdKey++); + private IndefiniteProgressTask getProgressTask(String prefixId, String title, String message) { + return this.progressService.createIndefiniteProgressTask(prefixId + progressIdKey++, title, message); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JavaReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JavaReconciler.java index 1b4f33faf..17fcb2f08 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JavaReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JavaReconciler.java @@ -24,6 +24,10 @@ public interface JavaReconciler { void reconcile(IJavaProject project, IDocument doc, IProblemCollector problemCollector); - Map> reconcile(IJavaProject project, List docs); + Map> reconcile(IJavaProject project, List docs, Runnable incrementProgress); + + default int getTotalWorkUnits(List docs) { + return 0; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtReconciler.java index c27b7c6b0..7401af35e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/JdtReconciler.java @@ -156,7 +156,7 @@ public class JdtReconciler implements JavaReconciler { } @Override - public Map> reconcile(IJavaProject project, List docs) { + public Map> reconcile(IJavaProject project, List docs, Runnable incrementProgress) { if (config.isRewriteReconcileEnabled()) { } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java index 1723a7ae5..f8089eaa9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteCompilationUnitCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 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 @@ -238,7 +238,7 @@ public class RewriteCompilationUnitCache implements DocumentContentProvider, Dis throw new IllegalStateException("Unexpected error fetching document content"); } }); - List cus = ORAstUtils.parseInputs(javaParser, List.of(input)); + List cus = ORAstUtils.parseInputs(javaParser, List.of(input), null); CompilationUnit cu = cus.get(0); if (cu != null) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java index d98e2bece..70e9d0bb7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java @@ -136,7 +136,7 @@ public class RewriteReconciler implements JavaReconciler { } @Override - public Map> reconcile(IJavaProject project, List docs) { + public Map> reconcile(IJavaProject project, List docs, Runnable incrementProgress) { if (!config.isRewriteReconcileEnabled()) { return Collections.emptyMap(); @@ -156,11 +156,12 @@ public class RewriteReconciler implements JavaReconciler { mainSources.add(d); } } + JavaParser javaParser = ORAstUtils.createJavaParser(project); javaParser.setSourceSet(MavenProjectParser.MAIN); - allProblems.putAll(doReconcile(project, mainSources, javaParser)); + allProblems.putAll(doReconcile(project, mainSources, javaParser, incrementProgress)); javaParser.setSourceSet(MavenProjectParser.TEST); - allProblems.putAll(doReconcile(project, testSources, javaParser)); + allProblems.putAll(doReconcile(project, testSources, javaParser, incrementProgress)); long end = System.currentTimeMillis(); log.info("reconciling project (OpenRewrite): " + project.getElementName() + " - " + docs.size() + " done in " + (end - start) + "ms"); @@ -168,6 +169,7 @@ public class RewriteReconciler implements JavaReconciler { return allProblems; } + // Parse all at once // private Map> doReconcile(IJavaProject project, List docs, // Function problemCollectorFactory, JavaParser javaParser) { @@ -240,7 +242,7 @@ public class RewriteReconciler implements JavaReconciler { private static final int BATCH = 50; // Parse in batches and share the parser - private Map> doReconcile(IJavaProject project, List docs, JavaParser javaParser) { + private Map> doReconcile(IJavaProject project, List docs, JavaParser javaParser, Runnable incrementProgress) { Map> allProblems = new HashMap<>(); if (javaParser != null && config.isRewriteReconcileEnabled()) { try { @@ -255,7 +257,7 @@ public class RewriteReconciler implements JavaReconciler { List cus = ORAstUtils.parseInputs(javaParser, batchList.stream().map(d -> new Parser.Input(Paths.get(URI.create(d.getUri())), () -> { return new ByteArrayInputStream(d.get().getBytes()); - })).collect(Collectors.toList())); + })).collect(Collectors.toList()), source -> incrementProgress.run()); /* * If exception occurs during parsing inputs the list of inputs would become shorter than the list of corresponding documents @@ -275,6 +277,7 @@ public class RewriteReconciler implements JavaReconciler { } else { log.warn("(OpenRewrite) Failed to parse source for " + sourcePath); } + incrementProgress.run(); } } @@ -327,5 +330,9 @@ public class RewriteReconciler implements JavaReconciler { }.visit(cu, new InMemoryExecutionContext(e -> log.error("", e))); } } - + + public int getTotalWorkUnits(List docs) { + return docs.size() * 2; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java index 150513227..9f7e4c474 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteRefactorings.java @@ -140,7 +140,8 @@ public class RewriteRefactorings implements CodeActionResolver, QuickfixHandler boolean projectWide = data.getRecipeScope() == RecipeScope.PROJECT; Recipe r = createRecipe(data); if (projectWide) { - return applyRecipe(r, project.get(), ORAstUtils.parse(documents, project.get())); + //TODO: progress here as well. + return applyRecipe(r, project.get(), ORAstUtils.parse(documents, project.get(), null)); } else { List cus = data.getDocUris().stream().map(docUri -> cuCache.getCU(project.get(), URI.create(docUri))).filter(Objects::nonNull).collect(Collectors.toList()); return applyRecipe(r, project.get(), cus); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java index 3ae72e794..359bb8a75 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/BootVersionValidationEngine.java @@ -20,6 +20,7 @@ import org.springframework.ide.vscode.boot.common.IJavaProjectReconcileEngine; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider; import org.springframework.ide.vscode.boot.validation.generations.ProjectVersionDiagnosticProvider.DiagnosticResult; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; 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; @@ -39,7 +40,7 @@ public class BootVersionValidationEngine implements IJavaProjectReconcileEngine this.diagnosticProvider = diagnosticProvider; } - public void reconcile(IJavaProject project) { + public void reconcile(IJavaProject project, ProgressService progressService) { if (config.isBootVersionValidationEnabled()) { log.debug("validating Spring Boot version on project: " + project.getElementName()); long start = System.currentTimeMillis();