diff --git a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelState.java b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelState.java index 72b7e21f..49501a96 100644 --- a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelState.java +++ b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelState.java @@ -20,7 +20,7 @@ import org.eclipse.glsp.server.model.GModelState; public interface EMFModelState extends GModelState { - void setEditingDomain(EditingDomain editingDomain); + void setEditingDomain(EditingDomain editingDomain, String subclientId); EditingDomain getEditingDomain(); diff --git a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelStateImpl.java b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelStateImpl.java index 0cfb1097..d88a1f4b 100644 --- a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelStateImpl.java +++ b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFModelStateImpl.java @@ -17,6 +17,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.resource.Resource; @@ -24,6 +25,7 @@ import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.glsp.graph.GModelIndex; import org.eclipse.glsp.graph.GModelRoot; +import org.eclipse.glsp.server.command.CommandStackManager; import org.eclipse.glsp.server.model.DefaultGModelState; import org.eclipse.glsp.server.session.ClientSession; import org.eclipse.glsp.server.session.ClientSessionListener; @@ -50,6 +52,9 @@ public class EMFModelStateImpl extends DefaultGModelState implements EMFModelSta @Inject protected ClientSessionManager clientSessionManager; + @Inject + protected CommandStackManager commandStackManager; + @Inject protected EMFIdGenerator idGenerator; @@ -62,9 +67,9 @@ public void init() { } @Override - public void setEditingDomain(final EditingDomain editingDomain) { + public void setEditingDomain(final EditingDomain editingDomain, final String subclientId) { this.editingDomain = editingDomain; - setCommandStack(this.editingDomain.getCommandStack()); + commandStackManager.setCommandStack(this.editingDomain.getCommandStack(), subclientId); } @Override @@ -107,8 +112,12 @@ protected void closeResourceSet() { } } if (result) { - commandStack.flush(); - saveIsDone(); + commandStackManager.getAllCommandStacks().forEach(commandStack -> { + commandStack.flush(); + if (commandStack instanceof BasicCommandStack) { + ((BasicCommandStack) commandStack).saveIsDone(); + } + }); } } diff --git a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFSourceModelStorage.java b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFSourceModelStorage.java index 5b24f3cf..d467454c 100644 --- a/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFSourceModelStorage.java +++ b/plugins/org.eclipse.glsp.server.emf/src/org/eclipse/glsp/server/emf/EMFSourceModelStorage.java @@ -56,17 +56,17 @@ public void loadSourceModel(final RequestModelAction action) { .orElseThrow(() -> new GLSPServerException("No source URI given to load model!")); URI resourceURI = URI.createFileURI(sourceURI); - EditingDomain editingDomain = getOrCreateEditingDomain(); + EditingDomain editingDomain = getOrCreateEditingDomain(action.getSubclientId()); doLoadSourceModel(editingDomain.getResourceSet(), resourceURI, action); } - protected EditingDomain getOrCreateEditingDomain() { + protected EditingDomain getOrCreateEditingDomain(final String subclientId) { if (modelState.getEditingDomain() != null) { return modelState.getEditingDomain(); } EditingDomain editingDomain = editingDomainFactory.createEditingDomain(); setupResourceSet(editingDomain.getResourceSet()); - modelState.setEditingDomain(editingDomain); + modelState.setEditingDomain(editingDomain, subclientId); return editingDomain; } diff --git a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF index 9fd776b6..1b343d54 100644 --- a/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.glsp.server/META-INF/MANIFEST.MF @@ -34,6 +34,7 @@ Export-Package: org.eclipse.glsp.server.actions, org.eclipse.glsp.server.features.validation, org.eclipse.glsp.server.gmodel, org.eclipse.glsp.server.gson, + org.eclipse.glsp.server.command, org.eclipse.glsp.server.internal.actions;x-internal:=true, org.eclipse.glsp.server.internal.di.scope;x-internal:=true, org.eclipse.glsp.server.internal.diagram;x-internal:=true, diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/Action.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/Action.java index 724fa2f9..1ea32038 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/Action.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/Action.java @@ -40,9 +40,20 @@ public abstract class Action { */ private boolean receivedFromClient; - public Action(final String kind) { + /** + * Unique identifier specifying the initiator of the action in a collaboration session. + * This value is initialized on the initating client. + */ + private String subclientId; + + public Action(final String kind, final String subclientId) { super(); this.kind = kind; + this.subclientId = subclientId; + } + + public Action(final String kind) { + this(kind, null); } public String getKind() { return kind; } @@ -51,6 +62,8 @@ public Action(final String kind) { public void setReceivedFromClient(final boolean receivedFromClient) { this.receivedFromClient = receivedFromClient; } + public String getSubclientId() { return subclientId; } + @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -60,4 +73,11 @@ public String toString() { return builder.toString(); } + public static Action addSubclientId(final Action initialAction, final Action extendedAction) { + if (initialAction.getSubclientId() != null) { + extendedAction.subclientId = initialAction.subclientId; + } + return extendedAction; + } + } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/SaveModelActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/SaveModelActionHandler.java index 4bd58ea2..44c72bdf 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/SaveModelActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/actions/SaveModelActionHandler.java @@ -45,11 +45,11 @@ public List executeAction(final SaveModelAction action) { modelSourceWatcher.ifPresent(watcher -> watcher.pauseWatching()); try { sourceModelStorage.saveSourceModel(action); - modelState.saveIsDone(); + modelState.saveIsDone(action.getSubclientId()); } finally { modelSourceWatcher.ifPresent(watcher -> watcher.continueWatching()); } - return listOf(new SetDirtyStateAction(modelState.isDirty(), SetDirtyStateAction.Reason.SAVE)); + return listOf(new SetDirtyStateAction(modelState.isDirty(action.getSubclientId()), SetDirtyStateAction.Reason.SAVE)); } } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackFactory.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackFactory.java new file mode 100644 index 00000000..9068ba62 --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackFactory.java @@ -0,0 +1,7 @@ +package org.eclipse.glsp.server.command; + +import org.eclipse.emf.common.command.CommandStack; + +public interface CommandStackFactory { + CommandStack createCommandStack(); +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackManager.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackManager.java new file mode 100644 index 00000000..2d810828 --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/CommandStackManager.java @@ -0,0 +1,13 @@ +package org.eclipse.glsp.server.command; + +import org.eclipse.emf.common.command.CommandStack; + +import java.util.List; + +public interface CommandStackManager { + CommandStack getOrCreateCommandStack(String subclientId); + + List getAllCommandStacks(); + + void setCommandStack(CommandStack commandStack, String subclientId); +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/DefaultCommandStackManager.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/DefaultCommandStackManager.java new file mode 100644 index 00000000..53c64ded --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/command/DefaultCommandStackManager.java @@ -0,0 +1,53 @@ +package org.eclipse.glsp.server.command; + +import com.google.inject.Inject; +import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.glsp.server.utils.CollaborationUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DefaultCommandStackManager implements CommandStackManager { + + @Inject + CommandStackFactory factory; + + // subclientId, CommandStack + protected Map commandStackMap = new HashMap<>(); + + @Override + public CommandStack getOrCreateCommandStack(final String subclientId) { + String subclientIdOrFallback = getSubclientIdOrFallback(subclientId); + if (commandStackMap.containsKey(subclientIdOrFallback)) { + return commandStackMap.get(subclientIdOrFallback); + } + + CommandStack commandStack = factory.createCommandStack(); + commandStackMap.put(subclientIdOrFallback, commandStack); + return commandStack; + } + + @Override + public List getAllCommandStacks() { + return new ArrayList<>(commandStackMap.values()); + } + + @Override + public void setCommandStack(final CommandStack commandStack, final String subclientId) { + String subclientIdOrFallback = getSubclientIdOrFallback(subclientId); + if (commandStackMap.containsKey(subclientIdOrFallback)) { + commandStackMap.get(subclientIdOrFallback).flush(); + } + commandStackMap.put(subclientIdOrFallback, commandStack); + } + + private String getSubclientIdOrFallback(final String subclientId) { + if (subclientId != null) { + return subclientId; + } + return CollaborationUtil.FALLBACK_SUBCLIENT_ID; + } + +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java index 95adc565..3720f6f4 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/di/DiagramModule.java @@ -62,10 +62,14 @@ import org.eclipse.glsp.server.gson.GraphGsonConfigurationFactory; import org.eclipse.glsp.server.internal.actions.DefaultActionDispatcher; import org.eclipse.glsp.server.internal.actions.DefaultActionHandlerRegistry; +import org.eclipse.glsp.server.command.CommandStackFactory; +import org.eclipse.glsp.server.command.CommandStackManager; +import org.eclipse.glsp.server.command.DefaultCommandStackManager; import org.eclipse.glsp.server.internal.diagram.DefaultServerConfigurationContribution; import org.eclipse.glsp.server.internal.featues.directediting.DefaultContextEditValidatorRegistry; import org.eclipse.glsp.server.internal.featues.navigation.DefaultNavigationTargetProviderRegistry; import org.eclipse.glsp.server.internal.features.contextactions.DefaultContextActionsProviderRegistry; +import org.eclipse.glsp.server.internal.gmodel.commandstack.GModelCommandStackFactory; import org.eclipse.glsp.server.internal.gson.DefaultGraphGsonConfigurationFactory; import org.eclipse.glsp.server.internal.operations.DefaultOperationHandlerRegistry; import org.eclipse.glsp.server.internal.toolpalette.DefaultToolPaletteItemProvider; @@ -189,6 +193,10 @@ protected void configureBase() { bindOptionally(LayoutEngine.class, bindLayoutEngine()); bindOptionally(GraphExtension.class, bindGraphExtension()); bindOptionally(EdgeCreationChecker.class, bindEdgeCreationChecker()); + + // Command Stack + bind(CommandStackFactory.class).to(bindCommandStackFactory()).in(Singleton.class); + bind(CommandStackManager.class).to(bindCommandStackManager()).in(Singleton.class); } protected void bindDiagramType() { @@ -328,5 +336,13 @@ protected Class bindEdgeCreationChecker() { return null; } + protected Class bindCommandStackFactory() { + return GModelCommandStackFactory.class; + } + + protected Class bindCommandStackManager() { + return DefaultCommandStackManager.class; + } + public abstract String getDiagramType(); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ComputedBoundsActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ComputedBoundsActionHandler.java index d4be507d..8c58d234 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ComputedBoundsActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/ComputedBoundsActionHandler.java @@ -45,7 +45,7 @@ public List executeAction(final ComputedBoundsAction action) { if (model != null && action.getRevision().isPresent() && action.getRevision().get().doubleValue() == model.getRevision()) { LayoutUtil.applyBounds(model, action, modelState); - return submissionHandler.submitModelDirectly(); + return submissionHandler.submitModelDirectly(action.getSubclientId()); } } return none(); 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..e7ebbbb4 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 @@ -88,7 +88,7 @@ public List submitInitialModel(final RequestModelAction requestAction) { * Therefore we temporarily store the action later retrival */ this.requestModelAction = Optional.of(requestAction); - return submitModel(); + return submitModel(requestAction.getSubclientId()); } @@ -103,7 +103,7 @@ public List submitInitialModel(final RequestModelAction requestAction) { * @param reason The optional reason that caused the model update. * @return A list of actions to be processed in order to submit the model. */ - public List submitModel(final String reason) { + public List submitModel(final String reason, final String subclientId) { modelFactory.createGModel(); int revision = this.requestModelAction.isPresent() ? 0 : this.modelState.getRoot().getRevision() + 1; modelState.getRoot().setRevision(revision); @@ -112,21 +112,21 @@ public List submitModel(final String reason) { if (needsClientLayout) { synchronized (modelLock) { return Arrays.asList(new RequestBoundsAction(modelState.getRoot()), - new SetDirtyStateAction(modelState.isDirty(), reason)); + new SetDirtyStateAction(modelState.isDirty(subclientId), reason)); } } - return submitModelDirectly(reason); + return submitModelDirectly(reason, subclientId); } - public List submitModel() { - return submitModel(null); + public List submitModel(final String subclientId) { + return submitModel(null, subclientId); } /** * Returns a list of actions to directly update the client-side model without any server- or client-side layouting. *

* Typically {@link ActionHandler action handlers} don't invoke this method but use - * {@link #submitModel(String)} + * {@link #submitModel(String, String)} * instead, as this is only used to eventually submit the model on the client directly after all layouting is already * performed before. The only foreseen caller of this method is {@link ComputedBoundsActionHandler}. *

@@ -139,7 +139,7 @@ public List submitModel() { * @param reason The optional reason that caused the model update. * @return A list of actions to be processed in order to submit the model. */ - public List submitModelDirectly(final String reason) { + public List submitModelDirectly(final String reason, final String subclientId) { GModelRoot gModel = modelState.getRoot(); if (diagramConfiguration.getLayoutKind() == ServerLayoutKind.AUTOMATIC && layoutEngine.isPresent()) { layoutEngine.get().layout(); @@ -152,7 +152,7 @@ public List submitModelDirectly(final String reason) { List result = new ArrayList<>(); result.add(modelAction); if (!diagramConfiguration.needsClientLayout()) { - result.add(new SetDirtyStateAction(modelState.isDirty(), reason)); + result.add(new SetDirtyStateAction(modelState.isDirty(subclientId), reason)); } if (validator.isPresent()) { List markers = validator.get() // @@ -163,8 +163,8 @@ public List submitModelDirectly(final String reason) { } } - public List submitModelDirectly() { - return submitModelDirectly(null); + public List submitModelDirectly(final String subclientId) { + return submitModelDirectly(null, subclientId); } protected SetModelAction createSetModeAction(final GModelRoot newRoot) { diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/RequestModelActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/RequestModelActionHandler.java index 42f4bec9..6d9d0ad1 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/RequestModelActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/core/model/RequestModelActionHandler.java @@ -64,20 +64,23 @@ public class RequestModelActionHandler extends AbstractActionHandler executeAction(final RequestModelAction action) { - modelState.setClientOptions(action.getOptions()); - - boolean isReconnecting = ClientOptionsUtil.isReconnecting(action.getOptions()); - - ProgressMonitor monitor = notifyStartLoading(); - if (isReconnecting) { - handleReconnect(action); - } else { - sourceModelStorage.loadSourceModel(action); - } - notifyFinishedLoading(monitor); - - if (!isReconnecting) { - sourceModelWatcher.ifPresent(watcher -> watcher.startWatching()); + // only reload if not initialized + if (!ClientOptionsUtil.disableReloadIsTrue(action.getOptions()) || modelState.getRoot() == null) { + modelState.setClientOptions(action.getOptions()); + + boolean isReconnecting = ClientOptionsUtil.isReconnecting(action.getOptions()); + + ProgressMonitor monitor = notifyStartLoading(); + if (isReconnecting) { + handleReconnect(action); + } else { + sourceModelStorage.loadSourceModel(action); + } + notifyFinishedLoading(monitor); + + if (!isReconnecting) { + sourceModelWatcher.ifPresent(watcher -> watcher.startWatching()); + } } return modelSubmissionHandler.submitInitialModel(action); diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/undoredo/UndoRedoActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/undoredo/UndoRedoActionHandler.java index fd203908..ddb94d75 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/undoredo/UndoRedoActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/features/undoredo/UndoRedoActionHandler.java @@ -39,12 +39,12 @@ public class UndoRedoActionHandler implements ActionHandler { @Override public List execute(final Action action) { - if (action instanceof UndoAction && modelState.canUndo()) { - modelState.undo(); - return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.UNDO); - } else if (action instanceof RedoAction && modelState.canRedo()) { - modelState.redo(); - return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.REDO); + if (action instanceof UndoAction && modelState.canUndo(action.getSubclientId())) { + modelState.undo(action.getSubclientId()); + return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.UNDO, action.getSubclientId()); + } else if (action instanceof RedoAction && modelState.canRedo(action.getSubclientId())) { + modelState.redo(action.getSubclientId()); + return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.REDO, action.getSubclientId()); } LOGGER.warn("Cannot undo or redo"); return none(); diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/AbstractGModelCreateNodeOperationHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/AbstractGModelCreateNodeOperationHandler.java index 960369e6..d7f21eb5 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/AbstractGModelCreateNodeOperationHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/AbstractGModelCreateNodeOperationHandler.java @@ -22,6 +22,7 @@ import org.eclipse.glsp.graph.GModelElement; import org.eclipse.glsp.graph.GNode; import org.eclipse.glsp.graph.GPoint; +import org.eclipse.glsp.server.actions.Action; import org.eclipse.glsp.server.actions.ActionDispatcher; import org.eclipse.glsp.server.actions.SelectAction; import org.eclipse.glsp.server.operations.AbstractCreateOperationHandler; @@ -59,7 +60,10 @@ public void executeOperation(final CreateNodeOperation operation) { .map(location -> LayoutUtil.getRelativeLocation(location, container)); GModelElement element = createNode(relativeLocation, operation.getArgs()); container.getChildren().add(element); - actionDispatcher.dispatchAfterNextUpdate(new SelectAction(), new SelectAction(List.of(element.getId()))); + actionDispatcher.dispatchAfterNextUpdate( + Action.addSubclientId(operation, new SelectAction()), + Action.addSubclientId(operation, new SelectAction(List.of(element.getId()))) + ); } /** diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/GModelCreateNodeOperationHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/GModelCreateNodeOperationHandler.java index 46a1f67d..e3921114 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/GModelCreateNodeOperationHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/gmodel/GModelCreateNodeOperationHandler.java @@ -24,6 +24,7 @@ import org.eclipse.glsp.graph.GModelElement; import org.eclipse.glsp.graph.GNode; import org.eclipse.glsp.graph.GPoint; +import org.eclipse.glsp.server.actions.Action; import org.eclipse.glsp.server.actions.ActionDispatcher; import org.eclipse.glsp.server.actions.GhostElement; import org.eclipse.glsp.server.actions.SelectAction; @@ -63,7 +64,12 @@ public void executeCreation(final CreateNodeOperation operation) { Optional relativeLocation = getRelativeLocation(container, absoluteLocation); GModelElement element = createNode(relativeLocation, operation.getArgs()); container.getChildren().add(element); - actionDispatcher.dispatchAfterNextUpdate(SelectAction.addSelection(List.of(element.getId()))); + actionDispatcher.dispatchAfterNextUpdate( + Action.addSubclientId( + operation, + SelectAction.addSelection(List.of(element.getId())) + ) + ); } protected Optional getLocation(final CreateNodeOperation operation) { diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/actions/DefaultActionDispatcher.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/actions/DefaultActionDispatcher.java index 6aed0da7..f89f6099 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/actions/DefaultActionDispatcher.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/actions/DefaultActionDispatcher.java @@ -197,6 +197,7 @@ protected List> runAction(final Action action) { for (final ActionHandler actionHandler : actionHandlers) { final List responses = actionHandler.execute(action).stream() .map(response -> ResponseAction.respond(action, response)) + .map(response -> Action.addSubclientId(action, response)) .collect(Collectors.toList()); results.addAll(dispatchAll(responses)); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/gmodel/commandstack/GModelCommandStackFactory.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/gmodel/commandstack/GModelCommandStackFactory.java new file mode 100644 index 00000000..70fa53fe --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/internal/gmodel/commandstack/GModelCommandStackFactory.java @@ -0,0 +1,11 @@ +package org.eclipse.glsp.server.internal.gmodel.commandstack; + +import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.glsp.server.command.CommandStackFactory; + +public class GModelCommandStackFactory implements CommandStackFactory { + @Override + public CommandStack createCommandStack() { + return new GModelCommandStack(); + } +} diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/DefaultGModelState.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/DefaultGModelState.java index a5991a80..93409511 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/DefaultGModelState.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/DefaultGModelState.java @@ -25,6 +25,7 @@ import org.eclipse.glsp.graph.GModelIndex; import org.eclipse.glsp.graph.GModelRoot; import org.eclipse.glsp.server.di.ClientId; +import org.eclipse.glsp.server.command.CommandStackManager; import org.eclipse.glsp.server.internal.gmodel.commandstack.GModelCommandStack; import com.google.inject.Inject; @@ -37,16 +38,18 @@ public class DefaultGModelState implements GModelState { @ClientId protected String clientId; + @Inject + protected CommandStackManager commandStackManager; + protected Map options; protected final Map properties = new HashMap<>(); protected GModelRoot currentModel; - protected CommandStack commandStack; protected String editMode; protected GModelIndex index = GModelIndex.empty(); @Inject public void init() { - setCommandStack(new GModelCommandStack()); + commandStackManager.setCommandStack(new GModelCommandStack(), null); } @Override @@ -73,13 +76,6 @@ protected GModelIndex getOrUpdateIndex(final GModelRoot newRoot) { protected void setRoot(final GModelRoot newRoot) { this.currentModel = newRoot; } - protected void setCommandStack(final CommandStack commandStack) { - if (this.commandStack != null) { - this.commandStack.flush(); - } - this.commandStack = commandStack; - } - @Override public void setClientOptions(final Map options) { this.options = options; } @@ -87,43 +83,50 @@ protected void setCommandStack(final CommandStack commandStack) { public GModelIndex getIndex() { return index; } @Override - public void execute(final Command command) { + public void execute(final Command command, String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); if (commandStack != null) { commandStack.execute(command); } } @Override - public boolean canUndo() { + public boolean canUndo(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); return commandStack != null && commandStack.canUndo(); } @Override - public boolean canRedo() { + public boolean canRedo(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); return commandStack != null && commandStack.canRedo(); } @Override - public void undo() { + public void undo(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); if (commandStack != null) { commandStack.undo(); } } @Override - public void redo() { + public void redo(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); if (commandStack != null) { commandStack.redo(); } } @Override - public boolean isDirty() { + public boolean isDirty(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); return commandStack instanceof BasicCommandStack && ((BasicCommandStack) commandStack).isSaveNeeded(); } @Override - public void saveIsDone() { + public void saveIsDone(final String subclientId) { + CommandStack commandStack = commandStackManager.getOrCreateCommandStack(subclientId); if (commandStack instanceof BasicCommandStack) { ((BasicCommandStack) commandStack).saveIsDone(); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/GModelState.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/GModelState.java index e9ea274d..70ffa118 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/GModelState.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/GModelState.java @@ -36,12 +36,12 @@ public interface GModelState extends ModelState { /** * Called after a save has been successfully performed. */ - void saveIsDone(); + void saveIsDone(String subclientId); /** * Uses its {@link BasicCommandStack} to execute a given {@link Command}. * * @param command The {@link Command} to be executed. */ - void execute(Command command); + void execute(Command command, String subclientId); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/ModelState.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/ModelState.java index 0a9598ff..731dcc79 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/ModelState.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/model/ModelState.java @@ -34,15 +34,15 @@ interface ModelState { void updateRoot(T newRoot); - boolean canUndo(); + boolean canUndo(String subclientId); - boolean canRedo(); + boolean canRedo(String subclientId); - void undo(); + void undo(String subclientId); - void redo(); + void redo(String subclientId); - boolean isDirty(); + boolean isDirty(String subclientId); String getEditMode(); diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/operations/OperationActionHandler.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/operations/OperationActionHandler.java index 82a12d80..1fa35e44 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/operations/OperationActionHandler.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/operations/OperationActionHandler.java @@ -62,18 +62,18 @@ protected List executeOperation(final Operation operation) { protected List executeHandler(final Operation operation, final OperationHandler handler) { Optional command = handler.execute(operation); if (command.isPresent()) { - exexcuteCommand(command.get()); - return submitModel(); + exexcuteCommand(command.get(), operation.getSubclientId()); + return submitModel(operation.getSubclientId()); } return none(); } - protected void exexcuteCommand(final Command command) { - modelState.execute(command); + protected void exexcuteCommand(final Command command, final String subclientId) { + modelState.execute(command, subclientId); } - protected List submitModel() { - return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.OPERATION); + protected List submitModel(final String subclientId) { + return modelSubmissionHandler.submitModel(SetDirtyStateAction.Reason.OPERATION, subclientId); } /** diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/ClientOptionsUtil.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/ClientOptionsUtil.java index 85550bef..aca30f28 100644 --- a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/ClientOptionsUtil.java +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/ClientOptionsUtil.java @@ -28,6 +28,8 @@ public final class ClientOptionsUtil { public static final String IS_RECONNECTING = "isReconnecting"; private static final String FILE_PREFIX = "file://"; + private static final String DISABLE_RELOAD = "disableReload"; + private ClientOptionsUtil() {} public static Optional getSourceUri(final Map options) { @@ -38,6 +40,15 @@ public static Optional getDiagramType(final Map options) return MapUtil.getValue(options, DIAGRAM_TYPE); } + public static Optional getDisableReload(final Map options) { + return MapUtil.getValue(options, DISABLE_RELOAD).map(Boolean::valueOf); + } + + public static Boolean disableReloadIsTrue(final Map options) { + Optional disableReload = getDisableReload(options); + return disableReload.isPresent() && disableReload.get(); + } + public static Optional getSourceUriAsFile(final Map options) { return MapUtil.getValue(options, SOURCE_URI).map(ClientOptionsUtil::getAsFile); } diff --git a/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/CollaborationUtil.java b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/CollaborationUtil.java new file mode 100644 index 00000000..82989403 --- /dev/null +++ b/plugins/org.eclipse.glsp.server/src/org/eclipse/glsp/server/utils/CollaborationUtil.java @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (c) 2020 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +package org.eclipse.glsp.server.utils; + +public class CollaborationUtil { + public static final String FALLBACK_SUBCLIENT_ID = "FALLBACK_SUBCLIENT_ID"; +}