@eclipse-emfcloud/model-manager
Version:
Command-based model editing with undo/redo.
121 lines • 5.3 kB
JavaScript
;
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://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: MIT License which is
// available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: EPL-2.0 OR MIT
// *****************************************************************************
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeferredCompoundCommand = void 0;
const model_logger_1 = require("@eclipse-emfcloud/model-logger");
const command_1 = require("../core/command");
const core_command_stack_impl_1 = require("./core-command-stack-impl");
const logger = (0, model_logger_1.getLogger)('model-manager/deferred-compound-command-impl');
/**
* Implementation of a compound command that permits deferred computation of its child
* commands with up-front declaration of the scope of models that it will edit.
*/
class DeferredCompoundCommand extends command_1.CompoundCommandImpl {
/**
* Initializes me with my scope and a function that prepares my child commands.
*
* @label my label, for presentation in the UI
* @param scope the scope of models that it is anticipated will be covered by the command
* when it is {@link prepare}d. This can be wider than what is eventually
* required but should not be narrower.
* @param commandProvider a function that provides an iterable over the commands to add and
* execute when it comes time to execute
* @param precondition an optional precondition mode to check the {@link canExecute} condition.
* This may be the constant `'strict'` to prepare my child commands and check them, or a
* predicate function to check without first having to prepare my commands. If omitted,
* precondition check is optimistically sidelined, just returning `true`
*/
constructor(label, modelScope, commandProvider, precondition) {
super(label);
this.commandProvider = commandProvider;
this.precondition = precondition;
/**
* Whether I have prepared my child commands. This is tracked as a separate state
* from my child commands because it cannot be inferred from them: my command
* provider may provide no commands and moreover commands may be statically added
* to me in the usual manner.
*/
this._prepared = false;
this._modelScope = [...modelScope];
}
async canExecute(getModel) {
if (this.isReady() && !this.isPrepared()) {
if (this.precondition === undefined) {
return true;
}
if (this.precondition === 'strict') {
await this.prepare(getModel);
return super.canExecute(getModel);
}
return this.precondition(getModel);
}
// If we've already been executed, then the standard rule applies
return super.canExecute(getModel);
}
async execute(getModel) {
await this.prepare(getModel);
return super.execute(getModel);
}
//
// Deferred Compound protocol
//
/**
* Query the scope of models edited by this command, including the set declared up-front
* and declared by any commands already added, either statically or by the deferred provider.
*/
get modelScope() {
const result = new Set(this._modelScope);
this._commands
.flatMap((next) => (0, core_command_stack_impl_1.getModelIds)(next))
.forEach((modelId) => result.add(modelId));
return Array.from(result);
}
/**
* Query whether I have prepared my member commands from my deferred provider.
*/
isPrepared() {
return this._prepared;
}
/**
* Prepare myself for execution by obtaining and appending the late-provided commands.
*
* @param getModel the model accessor to pass to my command provider
*/
async prepare(getModel) {
if (this.isPrepared()) {
// Only prepare once
return;
}
const childrenToAdd = await this.commandProvider(getModel);
this._prepared = true;
for (const next of childrenToAdd) {
const childToAdd = this.validate(await next);
if (childToAdd) {
this.append(childToAdd);
}
}
}
validate(command) {
const modelIds = (0, core_command_stack_impl_1.getModelIds)(command);
if (modelIds.some((modelId) => !this._modelScope.includes(modelId))) {
// For now, this is just a warning, not a validation failure
logger.warn(`Command '${command.label}' expands the model scope of deferred compound '${this.label}'.`);
}
return command;
}
}
exports.DeferredCompoundCommand = DeferredCompoundCommand;
//# sourceMappingURL=deferred-compound-command-impl.js.map