From 818ba4d6b1ee4f0a110619b6d825caa40e5e5948 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Fri, 18 Oct 2024 13:26:51 +0000 Subject: [PATCH] GLSP-1408: Make live validation asynchronous (#247) https://github.com/eclipse-glsp/glsp/issues/1408 --- .../core/model/ModelSubmissionHandler.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ModelSubmissionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ModelSubmissionHandler.java index 09a32589..59b9e77b 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ModelSubmissionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ModelSubmissionHandler.java @@ -15,6 +15,8 @@ ********************************************************************************/ package org.eclipse.glsp.server.features.core.model; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -33,6 +35,8 @@ import org.eclipse.glsp.server.layout.LayoutEngine; import org.eclipse.glsp.server.layout.ServerLayoutKind; import org.eclipse.glsp.server.model.GModelState; +import org.eclipse.glsp.server.utils.Debouncer; +import org.eclipse.glsp.server.utils.StatusActionUtil; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -61,9 +65,17 @@ public class ModelSubmissionHandler { @Inject protected GModelState modelState; + @Inject + protected ActionDispatcher actionDispatcher; + @Inject protected Optional validator; + protected Debouncer liveValidationDebouncer; + + // we use a very slight delay by default to avoid sending very short status messages for very fast validations + protected long liveValidationDelay = 100; + protected final Object modelLock = new Object(); protected Optional requestModelAction = Optional.empty(); @@ -155,14 +167,38 @@ public List submitModelDirectly(final String reason) { result.add(new SetDirtyStateAction(modelState.isDirty(), reason)); } if (validator.isPresent()) { - List markers = validator.get() // - .validate(Arrays.asList(modelState.getRoot()), MarkersReason.LIVE); - result.add(new SetMarkersAction(markers, MarkersReason.LIVE)); + result.addAll(validateModel(validator.get())); } return result; } } + protected List validateModel(final ModelValidator validator) { + scheduleLiveValidation(validator); + // we are using async live validation so there no actions to return for the model submission + return List.of(); + } + + protected void scheduleLiveValidation(final ModelValidator validator) { + if (liveValidationDebouncer == null) { + liveValidationDebouncer = new Debouncer<>(this::performLiveValidation, getLiveValidationDelay(), MILLISECONDS); + } + liveValidationDebouncer.accept(validator); + } + + public long getLiveValidationDelay() { return this.liveValidationDelay; } + + public void setLiveValidationDelay(final long liveValidationDelay) { + this.liveValidationDelay = liveValidationDelay; + } + + protected void performLiveValidation(final ModelValidator validator) { + actionDispatcher.dispatch(StatusActionUtil.info("Validate Model...")); + List markers = validator.validate(Arrays.asList(modelState.getRoot()), MarkersReason.LIVE); + SetMarkersAction markerAction = new SetMarkersAction(markers, MarkersReason.LIVE); + actionDispatcher.dispatchAll(List.of(markerAction, StatusActionUtil.clear())); + } + public List submitModelDirectly() { return submitModelDirectly(null); }