undoablelist.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { JSONValue } from '@phosphor/coreutils';
  4. import { each } from '@phosphor/algorithm';
  5. import { IObservableList, ObservableList } from './observablelist';
  6. /**
  7. * An object which knows how to serialize and
  8. * deserialize the type T.
  9. */
  10. export interface ISerializer<T> {
  11. /**
  12. * Convert the object to JSON.
  13. */
  14. toJSON(value: T): JSONValue;
  15. /**
  16. * Deserialize the object from JSON.
  17. */
  18. fromJSON(value: JSONValue): T;
  19. }
  20. /**
  21. * An observable list that supports undo/redo.
  22. */
  23. export interface IObservableUndoableList<T> extends IObservableList<T> {
  24. /**
  25. * Whether the object can redo changes.
  26. */
  27. readonly canRedo: boolean;
  28. /**
  29. * Whether the object can undo changes.
  30. */
  31. readonly canUndo: boolean;
  32. /**
  33. * Begin a compound operation.
  34. *
  35. * @param isUndoAble - Whether the operation is undoable.
  36. * The default is `false`.
  37. */
  38. beginCompoundOperation(isUndoAble?: boolean): void;
  39. /**
  40. * End a compound operation.
  41. */
  42. endCompoundOperation(): void;
  43. /**
  44. * Undo an operation.
  45. */
  46. undo(): void;
  47. /**
  48. * Redo an operation.
  49. */
  50. redo(): void;
  51. /**
  52. * Clear the change stack.
  53. */
  54. clearUndo(): void;
  55. }
  56. /**
  57. * A concrete implementation of an observable undoable list.
  58. */
  59. export class ObservableUndoableList<T> extends ObservableList<T>
  60. implements IObservableUndoableList<T> {
  61. /**
  62. * Construct a new undoable observable list.
  63. */
  64. constructor(serializer: ISerializer<T>) {
  65. super();
  66. this._serializer = serializer;
  67. this.changed.connect(this._onListChanged, this);
  68. }
  69. /**
  70. * Whether the object can redo changes.
  71. */
  72. get canRedo(): boolean {
  73. return this._index < this._stack.length - 1;
  74. }
  75. /**
  76. * Whether the object can undo changes.
  77. */
  78. get canUndo(): boolean {
  79. return this._index >= 0;
  80. }
  81. /**
  82. * Begin a compound operation.
  83. *
  84. * @param isUndoAble - Whether the operation is undoable.
  85. * The default is `true`.
  86. */
  87. beginCompoundOperation(isUndoAble?: boolean): void {
  88. this._inCompound = true;
  89. this._isUndoable = isUndoAble !== false;
  90. this._madeCompoundChange = false;
  91. }
  92. /**
  93. * End a compound operation.
  94. */
  95. endCompoundOperation(): void {
  96. this._inCompound = false;
  97. this._isUndoable = true;
  98. if (this._madeCompoundChange) {
  99. this._index++;
  100. }
  101. }
  102. /**
  103. * Undo an operation.
  104. */
  105. undo(): void {
  106. if (!this.canUndo) {
  107. return;
  108. }
  109. let changes = this._stack[this._index];
  110. this._isUndoable = false;
  111. for (let change of changes.reverse()) {
  112. this._undoChange(change);
  113. }
  114. this._isUndoable = true;
  115. this._index--;
  116. }
  117. /**
  118. * Redo an operation.
  119. */
  120. redo(): void {
  121. if (!this.canRedo) {
  122. return;
  123. }
  124. this._index++;
  125. let changes = this._stack[this._index];
  126. this._isUndoable = false;
  127. for (let change of changes) {
  128. this._redoChange(change);
  129. }
  130. this._isUndoable = true;
  131. }
  132. /**
  133. * Clear the change stack.
  134. */
  135. clearUndo(): void {
  136. this._index = -1;
  137. this._stack = [];
  138. }
  139. /**
  140. * Handle a change in the list.
  141. */
  142. private _onListChanged(
  143. list: IObservableList<T>,
  144. change: IObservableList.IChangedArgs<T>
  145. ): void {
  146. if (this.isDisposed || !this._isUndoable) {
  147. return;
  148. }
  149. // Clear everything after this position if necessary.
  150. if (!this._inCompound || !this._madeCompoundChange) {
  151. this._stack = this._stack.slice(0, this._index + 1);
  152. }
  153. // Copy the change.
  154. let evt = this._copyChange(change);
  155. // Put the change in the stack.
  156. if (this._stack[this._index + 1]) {
  157. this._stack[this._index + 1].push(evt);
  158. } else {
  159. this._stack.push([evt]);
  160. }
  161. // If not in a compound operation, increase index.
  162. if (!this._inCompound) {
  163. this._index++;
  164. } else {
  165. this._madeCompoundChange = true;
  166. }
  167. }
  168. /**
  169. * Undo a change event.
  170. */
  171. private _undoChange(change: IObservableList.IChangedArgs<JSONValue>): void {
  172. let index = 0;
  173. let serializer = this._serializer;
  174. switch (change.type) {
  175. case 'add':
  176. each(change.newValues, () => {
  177. this.remove(change.newIndex);
  178. });
  179. break;
  180. case 'set':
  181. index = change.oldIndex;
  182. each(change.oldValues, value => {
  183. this.set(index++, serializer.fromJSON(value));
  184. });
  185. break;
  186. case 'remove':
  187. index = change.oldIndex;
  188. each(change.oldValues, value => {
  189. this.insert(index++, serializer.fromJSON(value));
  190. });
  191. break;
  192. case 'move':
  193. this.move(change.newIndex, change.oldIndex);
  194. break;
  195. default:
  196. return;
  197. }
  198. }
  199. /**
  200. * Redo a change event.
  201. */
  202. private _redoChange(change: IObservableList.IChangedArgs<JSONValue>): void {
  203. let index = 0;
  204. let serializer = this._serializer;
  205. switch (change.type) {
  206. case 'add':
  207. index = change.newIndex;
  208. each(change.newValues, value => {
  209. this.insert(index++, serializer.fromJSON(value));
  210. });
  211. break;
  212. case 'set':
  213. index = change.newIndex;
  214. each(change.newValues, value => {
  215. this.set(change.newIndex++, serializer.fromJSON(value));
  216. });
  217. break;
  218. case 'remove':
  219. each(change.oldValues, () => {
  220. this.remove(change.oldIndex);
  221. });
  222. break;
  223. case 'move':
  224. this.move(change.oldIndex, change.newIndex);
  225. break;
  226. default:
  227. return;
  228. }
  229. }
  230. /**
  231. * Copy a change as JSON.
  232. */
  233. private _copyChange(
  234. change: IObservableList.IChangedArgs<T>
  235. ): IObservableList.IChangedArgs<JSONValue> {
  236. let oldValues: JSONValue[] = [];
  237. each(change.oldValues, value => {
  238. oldValues.push(this._serializer.toJSON(value));
  239. });
  240. let newValues: JSONValue[] = [];
  241. each(change.newValues, value => {
  242. newValues.push(this._serializer.toJSON(value));
  243. });
  244. return {
  245. type: change.type,
  246. oldIndex: change.oldIndex,
  247. newIndex: change.newIndex,
  248. oldValues,
  249. newValues
  250. };
  251. }
  252. private _inCompound = false;
  253. private _isUndoable = true;
  254. private _madeCompoundChange = false;
  255. private _index = -1;
  256. private _stack: IObservableList.IChangedArgs<JSONValue>[][] = [];
  257. private _serializer: ISerializer<T>;
  258. }
  259. /**
  260. * Namespace for ObservableUndoableList utilities.
  261. */
  262. export namespace ObservableUndoableList {
  263. /**
  264. * A default, identity serializer.
  265. */
  266. export class IdentitySerializer<T extends JSONValue>
  267. implements ISerializer<T> {
  268. /**
  269. * Identity serialize.
  270. */
  271. toJSON(value: T): JSONValue {
  272. return value;
  273. }
  274. /**
  275. * Identity deserialize.
  276. */
  277. fromJSON(value: JSONValue): T {
  278. return value as T;
  279. }
  280. }
  281. }