diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLLanguageServer.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLLanguageServer.java index 502cd5fb9..20c18d643 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLLanguageServer.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/XMLLanguageServer.java @@ -13,8 +13,6 @@ */ package org.eclipse.lemminx; -import static org.eclipse.lsp4j.jsonrpc.CompletableFutures.computeAsync; - import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -30,6 +28,7 @@ import org.eclipse.lemminx.client.ExtendedClientCapabilities; import org.eclipse.lemminx.commons.ModelTextDocument; import org.eclipse.lemminx.commons.ParentProcessWatcher.ProcessLanguageServer; +import org.eclipse.lemminx.commons.progress.ProgressSupport; import org.eclipse.lemminx.customservice.ActionableNotification; import org.eclipse.lemminx.customservice.AutoCloseTagResponse; import org.eclipse.lemminx.customservice.XMLLanguageClientAPI; @@ -67,9 +66,13 @@ import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.ProgressParams; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.SetTraceParams; import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.eclipse.lsp4j.WorkDoneProgressCreateParams; +import org.eclipse.lsp4j.WorkDoneProgressNotification; +import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.WorkspaceService; @@ -79,7 +82,7 @@ * */ public class XMLLanguageServer implements ProcessLanguageServer, XMLLanguageServerAPI, IXMLDocumentProvider, - IXMLNotificationService, IXMLValidationService { + IXMLNotificationService, IXMLValidationService, ProgressSupport { private static final Logger LOGGER = Logger.getLogger(XMLLanguageServer.class.getName()); @@ -101,6 +104,7 @@ public XMLLanguageServer() { xmlLanguageService.setNotificationService(this); xmlLanguageService.setCommandService(xmlWorkspaceService); xmlLanguageService.setValidationService(this); + xmlLanguageService.setProgressSupport(this); delayer = Executors.newScheduledThreadPool(1); } @@ -356,4 +360,20 @@ public void setTrace(SetTraceParams params) { // to avoid having error in vscode, the method is implemented // FIXME : implement the behavior of this method. } + + @Override + public boolean isWorkDoneProgressSupported() { + return capabilityManager.getClientCapabilities().isWorkDoneProgressSupported(); + } + + @Override + public CompletableFuture createProgress(WorkDoneProgressCreateParams params) { + return getLanguageClient().createProgress(params); + } + + @Override + public void notifyProgress(String progressId, WorkDoneProgressNotification notification) { + ProgressParams params = new ProgressParams(Either.forLeft(progressId), Either.forRight(notification)); + getLanguageClient().notifyProgress(params); + } } diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressMonitor.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressMonitor.java new file mode 100644 index 000000000..427a9d7e0 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressMonitor.java @@ -0,0 +1,90 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.commons.progress; + +import java.util.UUID; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.WorkDoneProgressBegin; +import org.eclipse.lsp4j.WorkDoneProgressCreateParams; +import org.eclipse.lsp4j.WorkDoneProgressEnd; +import org.eclipse.lsp4j.WorkDoneProgressReport; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.messages.Either; + +/** + * Progress monitor which wraps the LSP progress API to simplify the + * implementation of progress. + * + * @author Angelo ZERR + * + */ +public class ProgressMonitor implements CancelChecker { + + private final String progressId; + + private final ProgressSupport progressSupport; + + private CompletableFuture future; + + public ProgressMonitor(ProgressSupport progressSupport) { + this(UUID.randomUUID().toString(), progressSupport); + } + + public ProgressMonitor(String progressId, ProgressSupport progressSupport) { + this.progressId = progressId; + this.progressSupport = progressSupport; + + // Initialize progress + WorkDoneProgressCreateParams create = new WorkDoneProgressCreateParams(Either.forLeft(progressId)); + future = progressSupport.createProgress(create); + } + + public void begin(String title, String message, Integer percentage, Boolean cancellable) { + // Start progress + WorkDoneProgressBegin begin = new WorkDoneProgressBegin(); + begin.setTitle(title); + begin.setMessage(message); + begin.setPercentage(percentage); + begin.setCancellable(cancellable); + progressSupport.notifyProgress(progressId, begin); + } + + public void report(String message, Integer percentage, Boolean cancellable) { + // report message + WorkDoneProgressReport report = new WorkDoneProgressReport(); + report.setMessage(message); + report.setPercentage(percentage); + report.setCancellable(cancellable); + progressSupport.notifyProgress(progressId, report); + } + + public void end(String message) { + WorkDoneProgressEnd end = new WorkDoneProgressEnd(); + end.setMessage(message); + progressSupport.notifyProgress(progressId, end); + } + + @Override + public void checkCanceled() { + if (future != null && future.isCancelled()) { + throw new CancellationException(); + } + } + + @Override + public boolean isCanceled() { + return future != null ? future.isCancelled() : true; + } + +} diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressSupport.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressSupport.java new file mode 100644 index 000000000..07d5884a0 --- /dev/null +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/commons/progress/ProgressSupport.java @@ -0,0 +1,63 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lemminx.commons.progress; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.WorkDoneProgressCreateParams; +import org.eclipse.lsp4j.WorkDoneProgressNotification; + +/** + * LSP Progress support API. + * + * @author Angelo ZERR + * + */ +public interface ProgressSupport { + + default ProgressMonitor createProgressMonitor() { + if (!isWorkDoneProgressSupported()) { + return null; + } + return new ProgressMonitor(this); + } + + default ProgressMonitor createProgressMonitor(String progressId) { + if (!isWorkDoneProgressSupported()) { + return null; + } + return new ProgressMonitor(progressId, this); + } + + /** + * Returns true if the LSP client can support LSP progress and false otherwise. + * + * @return true if the LSP client can support LSP progress and false otherwise. + */ + boolean isWorkDoneProgressSupported(); + + /** + * Create a progress. + * + * @param params the progress create parameters + * @return + */ + CompletableFuture createProgress(WorkDoneProgressCreateParams params); + + /** + * Notify the the progress notification with the given process id. + * + * @param progressId the progress id. + * @param notification the progress notification. + */ + void notifyProgress(String progressId, WorkDoneProgressNotification notification); +} \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java index 2f0ff4763..7cb1a458a 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/services/extensions/XMLExtensionsRegistry.java @@ -23,6 +23,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.eclipse.lemminx.commons.progress.ProgressSupport; import org.eclipse.lemminx.services.IXMLDocumentProvider; import org.eclipse.lemminx.services.IXMLNotificationService; import org.eclipse.lemminx.services.IXMLValidationService; @@ -70,7 +71,8 @@ public class XMLExtensionsRegistry implements IComponentProvider { private IXMLDocumentProvider documentProvider; private IXMLValidationService validationService; private IXMLCommandService commandService; - + private ProgressSupport progressSupport; + private InitializeParams params; private ISaveContext initialSaveContext; @@ -552,5 +554,13 @@ public TelemetryManager getTelemetryManager() { public void setTelemetryManager(TelemetryManager telemetryManager) { this.telemetryManager = telemetryManager; } + + public void setProgressSupport(ProgressSupport progressSupport) { + this.progressSupport = progressSupport; + } + + public ProgressSupport getProgressSupport() { + return progressSupport; + } } \ No newline at end of file diff --git a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java index 9b5f404ec..12ba33097 100644 --- a/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java +++ b/org.eclipse.lemminx/src/main/java/org/eclipse/lemminx/settings/capabilities/ClientCapabilitiesWrapper.java @@ -155,4 +155,9 @@ public boolean shouldLanguageServerExitOnShutdown() { } return extendedCapabilities.shouldLanguageServerExitOnShutdown(); } + + public boolean isWorkDoneProgressSupported() { + return v3Supported && capabilities.getWindow() != null && capabilities.getWindow().getWorkDoneProgress() != null + && capabilities.getWindow().getWorkDoneProgress().booleanValue(); + } } \ No newline at end of file