@lcap/asl
Version:
NetEase Application Specific Language
389 lines (369 loc) • 14.4 kB
text/typescript
import { ACTION_MODE, App, vertexsMap } from '..';
import undoRedoService from '../../service/undoredo';
import Vertex from '../common/Vertex';
import { LogicItem } from '../logic/LogicItem';
export interface Operation {
operationAction: string;
operationDesc: string;
operationBeforeImage: any;
operationAfterImage: any;
activeImage?: any
}
export interface OperationHandler {
context: Vertex | { new(): LogicItem },
undoAction: string,
undoActionParam: string,
redoAction: string,
redoActionParam: string,
}
/**
* 后续考虑优化可逆操作
* @param operation
* @returns
*/
function getOperationHandler(operation: Operation) {
const map: { [action: string]: () => OperationHandler } = {
/* Element */
'Element.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.parentId),
undoAction: 'Element.removeChild',
undoActionParam: 'After',
redoAction: 'Element.addChild',
redoActionParam: 'After',
}),
'Element.addChild': () => ({
context: vertexsMap.get(operation.operationAfterImage.parentId),
undoAction: 'Element.removeChild',
undoActionParam: 'After',
redoAction: 'Element.addChild',
redoActionParam: 'After',
}),
'Element.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.parentId),
undoAction: 'Element.addChild',
undoActionParam: 'Before',
redoAction: 'Element.removeChild',
redoActionParam: 'Before',
}),
'Element.removeChild': () => ({
context: vertexsMap.get(operation.operationBeforeImage.parentId),
undoAction: 'Element.addChild',
undoActionParam: 'Before',
redoAction: 'Element.removeChild',
redoActionParam: 'Before',
}),
'Element.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Element.update',
undoActionParam: 'Before',
redoAction: 'Element.update',
redoActionParam: 'After',
}),
'Element.move': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Element.move',
undoActionParam: 'Before',
redoAction: 'Element.move',
redoActionParam: 'After',
}),
/* Attr */
'Attr.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteAttr',
undoActionParam: 'After',
redoAction: 'Element.addAttr',
redoActionParam: 'After',
}),
'Element.addAttr': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteAttr',
undoActionParam: 'After',
redoAction: 'Element.addAttr',
redoActionParam: 'After',
}),
'Attr.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addAttr',
undoActionParam: 'Before',
redoAction: 'Element.deleteAttr',
redoActionParam: 'Before',
}),
'Element.deleteAttr': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addAttr',
undoActionParam: 'Before',
redoAction: 'Element.deleteAttr',
redoActionParam: 'Before',
}),
'Attr.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Attr.update',
undoActionParam: 'Before',
redoAction: 'Attr.update',
redoActionParam: 'After',
}),
/* Event */
'Event.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteEvent',
undoActionParam: 'After',
redoAction: 'Element.addEvent',
redoActionParam: 'After',
}),
'Element.addEvent': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteEvent',
undoActionParam: 'After',
redoAction: 'Element.addEvent',
redoActionParam: 'After',
}),
'Event.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addEvent',
undoActionParam: 'Before',
redoAction: 'Element.deleteEvent',
redoActionParam: 'Before',
}),
'Element.deleteEvent': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addEvent',
undoActionParam: 'Before',
redoAction: 'Element.deleteEvent',
redoActionParam: 'Before',
}),
'Event.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Event.update',
undoActionParam: 'Before',
redoAction: 'Event.update',
redoActionParam: 'After',
}),
/* Directive */
'Directive.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteDirective',
undoActionParam: 'After',
redoAction: 'Element.addDirective',
redoActionParam: 'After',
}),
'Element.addDirective': () => ({
context: vertexsMap.get(operation.operationAfterImage.elementId),
undoAction: 'Element.deleteDirective',
undoActionParam: 'After',
redoAction: 'Element.addDirective',
redoActionParam: 'After',
}),
'Directive.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addDirective',
undoActionParam: 'Before',
redoAction: 'Element.deleteDirective',
redoActionParam: 'Before',
}),
'Element.deleteDirective': () => ({
context: vertexsMap.get(operation.operationBeforeImage.elementId),
undoAction: 'Element.addDirective',
undoActionParam: 'Before',
redoAction: 'Element.deleteDirective',
redoActionParam: 'Before',
}),
'Directive.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Directive.update',
undoActionParam: 'Before',
redoAction: 'Directive.update',
redoActionParam: 'After',
}),
/* Lifecycle */
'Lifecycle.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.viewId),
undoAction: 'Element.removeLifecycle',
undoActionParam: 'After',
redoAction: 'Element.addLifecycle',
redoActionParam: 'After',
}),
'View.addLifecycle': () => ({
context: vertexsMap.get(operation.operationAfterImage.viewId),
undoAction: 'View.removeLifecycle',
undoActionParam: 'After',
redoAction: 'View.addLifecycle',
redoActionParam: 'After',
}),
'Lifecycle.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.viewId),
undoAction: 'View.addLifecycle',
undoActionParam: 'Before',
redoAction: 'View.removeLifecycle',
redoActionParam: 'Before',
}),
'View.removeLifecycle': () => ({
context: vertexsMap.get(operation.operationBeforeImage.viewId),
undoAction: 'View.addLifecycle',
undoActionParam: 'Before',
redoAction: 'View.removeLifecycle',
redoActionParam: 'Before',
}),
'Lifecycle.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'Lifecycle.update',
undoActionParam: 'Before',
redoAction: 'Lifecycle.update',
redoActionParam: 'After',
}),
/* LogicItem */
'LogicItem.create': () => ({
context: vertexsMap.get(operation.operationAfterImage.logicId),
undoAction: 'Logic.removeItem',
undoActionParam: 'After',
redoAction: 'Logic.addItem',
redoActionParam: 'After',
}),
'Logic.addItem': () => ({
context: vertexsMap.get(operation.operationAfterImage.logicId),
undoAction: 'Logic.removeItem',
undoActionParam: 'After',
redoAction: 'Logic.addItem',
redoActionParam: 'After',
}),
'LogicItem.delete': () => ({
context: vertexsMap.get(operation.operationBeforeImage.logicId),
undoAction: 'Logic.addItem',
undoActionParam: 'Before',
redoAction: 'Logic.removeItem',
redoActionParam: 'Before',
}),
'LogicItem.removeItem': () => ({
context: vertexsMap.get(operation.operationBeforeImage.logicId),
undoAction: 'Logic.addItem',
undoActionParam: 'Before',
redoAction: 'Logic.removeItem',
redoActionParam: 'Before',
}),
'LogicItem.update': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'LogicItem.update',
undoActionParam: 'Before',
redoAction: 'LogicItem.update',
redoActionParam: 'After',
}),
'LogicItem.move': () => ({
context: vertexsMap.get(operation.operationBeforeImage.id),
undoAction: 'LogicItem.move',
undoActionParam: 'Before',
redoAction: 'LogicItem.move',
redoActionParam: 'After',
}),
'LogicItem.paste': () => ({
context: LogicItem,
undoAction: 'View.undoPaste',
undoActionParam: 'After',
redoAction: 'View.redoPaste',
redoActionParam: 'After',
}),
'View.mergeBlock': () => ({
context: vertexsMap.get(operation.operationAfterImage.id),
undoAction: 'View.undoMergeBlock',
undoActionParam: 'After',
redoAction: 'View.redoMergeBlock',
redoActionParam: 'After',
}),
};
return map[operation.operationAction]();
}
export class History {
app: App = undefined;
undoCount: number = 0;
redoCount: number = 0;
undoStack: Array<Operation> = [];
redoStack: Array<Operation> = [];
constructor(source: any) {
Object.assign(this, source);
}
/**
* 加载 undoRedo 栈
* @param operation 失败时用于前端模拟
*/
async load(operation?: Operation) {
try {
const result = await undoRedoService.undoRedoInfo({
headers: {
appId: this.app.id,
},
});
this.undoCount = result.undoNumber;
this.redoCount = result.redoNumber;
} catch (e) { }
}
async undo() {
this.app.emit('saving');
let operation: Operation;
try {
const result = await undoRedoService.undo({
headers: {
appId: this.app.id,
},
});
operation = result.operation;
} catch (e) {
return;
}
const oh = getOperationHandler(operation);
let image = (operation as any)[`operation${oh.undoActionParam}Image`];
if (operation.operationAction === 'LogicItem.move') {
// 移动 QueryJoinExpression 会同时修改 onExpressionList
if (image.type === 'QueryJoinExpression') {
const context = oh.context as LogicItem;
context.assign(LogicItem.from(image, context.logic, context.parent));
}
image = image.moveToLogicItem;
}
operation.activeImage = image;
if (operation.operationAction === 'LogicItem.paste')
operation.activeImage = operation.operationAfterImage.logicItems[0];
await (oh.context as any)[oh.undoAction.split('.').pop()](image, {
actionMode: ACTION_MODE.undoRedo,
});
return operation;
}
async redo() {
this.app.emit('saving');
let operation: Operation;
try {
const result = await undoRedoService.redo({
headers: {
appId: this.app.id,
},
});
operation = result.operation;
} catch (e) {
return;
}
const oh = getOperationHandler(operation);
let image = (operation as any)[`operation${oh.redoActionParam}Image`];
if (operation.operationAction === 'LogicItem.move') {
// 移动 QueryJoinExpression 会同时修改 onExpressionList
const { operationBeforeImage } = operation;
if (operationBeforeImage.type === 'QueryJoinExpression') {
const context = <any>oh.context;
context.onExpressionList = [LogicItem.from(operationBeforeImage.onExpressionList[0], context.logic, context)];
context.onExpressionList[0].left.propertyRef = '';
context.onExpressionList[0].right.propertyRef = '';
}
image = image.moveToLogicItem;
}
operation.activeImage = image;
if (operation.operationAction === 'LogicItem.paste')
operation.activeImage = operation.operationAfterImage.logicItems[0];
await (oh.context as any)[oh.redoAction.split('.').pop()](image, {
actionMode: ACTION_MODE.undoRedo,
});
return operation;
}
clear() {
this.undoStack = [];
this.redoStack = [];
this.undoCount = 0;
this.redoCount = 0;
}
}
export default History;