UNPKG

@eclipse-emfcloud/model-manager

Version:

Command-based model editing with undo/redo.

246 lines 12.8 kB
import type { Command, CommandResult } from './command'; import type { CoreCommandStackSubscription } from './command-stack-subscription'; import type { EditingContext } from './editing-context'; /** * Enumeration of the operations for which a command stack maintains history. */ export type UndoRedoOp = 'undo' | 'redo'; /** * A type derived from any operation that is the indication of whether that * operation _can_ be performed. */ export type Can<Op extends string> = `can${Capitalize<Op>}`; /** * An analysis report of the undo/redo state of some {@link EditingContext} in the {@link CoreCommandStack}. * The report is summarized by a `boolean` property named either `canUndo` or `canRedo` according to whether * the analysis was for the undo or redo of an editing context and in addition has details about context * dependencies, if applicable. * * @see {@link CoreCommandStack.analyzeUndo()} * @see {@link CoreCommandStack.analyzeRedo()} */ export type CommandAnalysis<Op extends UndoRedoOp> = Record<Can<Op>, boolean> & { /** * Whether the performability of undo/redo is contingent on dependencies on other contexts. * Those dependencies, if any, are included in the `contexts` object. */ hasDependencies: boolean; /** * A convenient message summarizing the analysis result. */ summary: string; /** * An object indicating per editing context whether that context is undoable or not. * This includes the initial context that was analyzed and also any that its undo or redo depends * on also being undone or redone. This lets a client know the reason why a context, even accounting * for dependencies, is not undoable or redoable. * Note that the editing context for which the analysis was requested may itself not be undoable or * redoable even if all of its dependencies are. * This object will always include at least the editing context that was analyzed, and only that * if it has no dependencies. */ contexts: Record<EditingContext, boolean>; }; /** The analysis report for undo of an editing context. */ export type UndoAnalysis = CommandAnalysis<'undo'>; /** The analysis report for redo of an editing context. */ export type RedoAnalysis = CommandAnalysis<'redo'>; /** * An ordered history of the execution of commands on the models managed by a `CoreModelManager`. * * @template K the type of model ID used by the Model Manager */ export interface CoreCommandStack<K = string> { /** * Execute a command in one or more editing `contexts`. * If successful, that command becomes the next available to {@link undo}. * * The result is a mapping of commands to the changes that they applied to their target models, * which accounts for the case that the `command` is a compound. * * @param command a command to execute * @param contexts one or more editing contexts to associate with the execution of the `command` * @returns a description of the changes performed by the `command`, if available * @throws if the `command` is not executable or if no `contexts` are specified */ execute(command: Command<K>, ...contexts: EditingContext[]): CommandResult<K>; /** * Execute a command in one or more editing `contexts` and append it to the command most * recently executed in the given appended context. * If successful, that command is integrated into the appended command for {@link undo} and {@link redo} * and the appended command's editing contexts are united with the given additional `contexts`. * * As for {@link execute}, the result is a mapping of commands to the changes that they applied to their * target models, which accounts for the case that the `command` is a compound. * * @param appendContext the editing context in which to append the `command` to its most recently executed command * @param command a command to execute * @param contexts one or more editing contexts to associate with the execution of the `command` * @returns a description of the changes performed by the `command`, if available * @throws if the `command` is not executable, if no `contexts` are specified, or if there has been no command * executed in the given appended context */ executeAndAppend(appendedContext: EditingContext, command: Command<K>, ...contexts: EditingContext[]): CommandResult<K>; /** * Undo the command most recently executed/redone in the given editing `context`. * If successful, that command becomes the next available to {@link redo}. * Moreover, if `withDependencies` is `true` and there are dependencies that have to * be undone before the `context` command, then they are aggregated together with it in * a new compound that replaces them all atop the redo stack. * * @param context the editing context to undo * @param [withDependencies] whether to include dependencies if there are any. * The default is `true`. Pass `false` to reject undo if there are dependencies * @returns a description of the changes performed by undo, if available * @throws if there is no command to undo in the given `context` or if that command is not undoable * * @see {@link getUndoCommand} * @see {@link canUndo} */ undo(context: EditingContext, withDependencies?: boolean): CommandResult<K>; /** * Redo the command most recently undone in the given editing `context`. * If successful, that command becomes the next available to {@link undo}. * Moreover, if `withDependencies` is `true` and there are dependencies that have to * be redone before the `context` command, then they are aggregated together with it in * a new compound that replaces them all atop the redo stack. * * @param context the editing context to redo * @param [withDependencies] whether to include dependencies if there are any. * The default is `true`. Pass `false` to reject redo if there are dependencies * @returns a description of the changes performed by redo, if available * @throws if there is no command to redo in the given `context` or if that command is not redoable * * @see {@link getRedoCommand} * @see {@link canRedo} */ redo(context: EditingContext, withDependencies?: boolean): CommandResult<K>; /** * Query whether a `command` can be executed in the given editing `context`s. * * @param command a command to be executed * @param contexts the editing contexts in which the command is to be executed * @returns whether the `command` can be executed in the editing `context`s * @see {@link execute} */ canExecute(command: Command<K>, ...contexts: EditingContext[]): Promise<boolean>; /** * Query whether a given editing `context` has a command that can be undone. * * @param context the editing context to undo * @param [withDependencies] whether to include dependencies if there are any. * The default is `true`. Pass `false` to refuse undo if there are dependencies * @returns whether the editing `context` has a command to undo and that command is undoable * @see {@link getUndoCommand} */ canUndo(context: EditingContext, withDependencies?: boolean): Promise<boolean>; /** * Analyze the undoability of the {@link getUndoCommand undo command} for a given editing `context`. * * @param context and editing context that may or may not {@link canUndo be undoable} * @returns a detailed analysis of the undoability of the context * @see {@link getUndoCommand()} * @see {@link canUndo()} */ analyzeUndo(context: EditingContext): Promise<UndoAnalysis>; /** * Query whether a given editing `context` has a command that can be redone. * * @param context the editing context to redo * @param [withDependencies] whether to include dependencies if there are any. * The default is `true`. Pass `false` to refuse redo if there are dependencies * @returns whether the editing `context` has a command to redo and that command is redoable * @see {@link getRedoCommand} */ canRedo(context: EditingContext, withDependencies?: boolean): Promise<boolean>; /** * Analyze the redoability of the {@link getRedoCommand redo command} for a given editing `context`. * * @param context and editing context that may or may not {@link canRedo be redoable} * @returns a detailed analysis of the redoability of the context * @see {@link getRedoCommand()} * @see {@link canRedo()} */ analyzeRedo(context: EditingContext): Promise<RedoAnalysis>; /** * Query what is the next command that would be undone by a request to {@link undo} a given editing `context`. * * @param context the editing context to undo * @returns the next undoable command in the editing `context`, or `undefined` if none */ getUndoCommand(context: EditingContext): Command<K> | undefined; /** * Query what is the next command that would be redone by a request to {@link redo} a given editing `context`. * * @param context the editing context to redo * @returns the next redoable command in the editing `context`, or `undefined` if none */ getRedoCommand(context: EditingContext): Command<K> | undefined; /** * Flush an editing `context`. * The given `context` is removed from the history of all commands that were associated with it. * Any commands that are then left without any associated editing contexts are purged from the stack * and returned to the caller. * * @param context an editing context to flush * @return the commands that were purged from the stack, or an empty array if none */ flush(context: EditingContext): Command<K>[]; /** * Mark an editing `context` as having been saved. * This should be done by a client that has saved the models edited in that `context` * to persistent storage. * * @param editingContext a context to mark as saved * * @see {@link isDirty} * @see {@link getDirtyModels} */ markSaved(editingContext: EditingContext): void; /** * Queries whether any commands have been executed, undone, or redone * in the given context since it was last {@link markSaved saved}. * * @param editingContext a context to test for dirty state * @returns whether it is dirty * * @see {@link markSaved} * @see {@link getDirtyModelIds} */ isDirty(editingContext: EditingContext): boolean; /** * Query the IDs of models that have been modified by execution, undo, and redo * of commands in the given `context` since the last {@link markSaved save}. * This may include models that are assumed to be dirty because they were * modified at the time this `context` was last flushed. * * @param editingContext an editing context for which to obtain the dirty models * * @see {@link markSaved} * @see {@link isDirty} */ getDirtyModelIds(editingContext: EditingContext): K[]; /** * Query the editing contexts that currently have associated command histories and/or dirty state. * If an editing context has been {@linkplain flush flushed} but still {@linkplain getDirtyModels is dirty}, then it will be returned in the result. * Otherwise, the editing context effectively no longer exists and will not be returned in the result. */ getEditingContexts(): EditingContext[]; /** * Create a subscription to the changes occurring in a particular editing context or all contexts. * Each call creates a new subscription that, when no longer needed, {@link CoreCommandStackSubscription.close should be closed} separately. * * If an `editingContext` is specified, notifications of changes only in that context will be sent to the * resulting subscription. * Otherwise, it will receive notification of changes to all contexts. * * The `editingContext` needs not necessarily be associated with any command history at the time of subscription. * The subscription will simply never receive notifications until such time as the context begins to see operations on commands. * * @param [editingContext] the optional editing context to which to subscribe. * If omitted or `undefined`, the subscription will notify on changes to all editing contexts that I manage * @returns the command-stack subscription */ subscribe(editingContext?: EditingContext): CoreCommandStackSubscription<K>; } //# sourceMappingURL=core-command-stack.d.ts.map