diff --git a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart index 44c36f7d16..1e6e404385 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart @@ -6,9 +6,12 @@ import 'package:flutter/material.dart'; import './document/selection.dart'; class ApplyOptions { - final bool noLog; + /// This flag indicates that + /// whether the transaction should be recorded into + /// the undo stack. + final bool recordUndo; const ApplyOptions({ - this.noLog = false, + this.recordUndo = true, }); } @@ -45,19 +48,16 @@ class EditorState { } cursorSelection = transaction.afterSelection; - if (options.noLog) { - return; + if (options.recordUndo) { + final undoItem = undoManager.getUndoHistoryItem(); + undoItem.addAll(transaction.operations); + if (undoItem.beforeSelection == null && + transaction.beforeSelection != null) { + undoItem.beforeSelection = transaction.beforeSelection; + } + undoItem.afterSelection = transaction.afterSelection; + _debouncedSealHistoryItem(); } - - final undoItem = undoManager.getUndoHistoryItem(); - undoItem.addAll(transaction.operations); - if (undoItem.beforeSelection == null && - transaction.beforeSelection != null) { - undoItem.beforeSelection = transaction.beforeSelection; - } - undoItem.afterSelection = transaction.afterSelection; - - _debouncedSealHistoryItem(); } _debouncedSealHistoryItem() { diff --git a/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart b/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart index b523ba54a7..e85cebe54a 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/undo_manager.dart @@ -6,6 +6,9 @@ import 'package:flowy_editor/operation/transaction_builder.dart'; import 'package:flowy_editor/operation/transaction.dart'; import 'package:flowy_editor/editor_state.dart'; +/// This class contains operations to committed by users. +/// If a [HistoryItem] is not sealed, operations can be added sequentially. +/// Otherwise, the operations should be added to a new [HistoryItem]. class HistoryItem extends LinkedListEntry { final List operations = []; Selection? beforeSelection; @@ -18,6 +21,8 @@ class HistoryItem extends LinkedListEntry { _sealed = true; } + bool get sealed => _sealed; + add(Operation op) { operations.add(op); } @@ -26,10 +31,6 @@ class HistoryItem extends LinkedListEntry { operations.addAll(iterable); } - bool get sealed { - return _sealed; - } - Transaction toTransaction(EditorState state) { final builder = TransactionBuilder(state); for (var i = operations.length - 1; i >= 0; i--) { @@ -67,24 +68,22 @@ class FixedSizeStack { return last; } - HistoryItem get last { - return _list.last; - } + HistoryItem get last => _list.last; - bool get isEmpty { - return _list.isEmpty; - } + bool get isEmpty => _list.isEmpty; - bool get isNonEmpty { - return _list.isNotEmpty; - } + bool get isNonEmpty => _list.isNotEmpty; } class UndoManager { - final undoStack = FixedSizeStack(20); - final redoStack = FixedSizeStack(20); + final FixedSizeStack undoStack; + final FixedSizeStack redoStack; EditorState? state; + UndoManager([int stackSize = 20]) + : undoStack = FixedSizeStack(stackSize), + redoStack = FixedSizeStack(stackSize); + HistoryItem getUndoHistoryItem() { if (undoStack.isEmpty) { final item = HistoryItem(); @@ -101,11 +100,15 @@ class UndoManager { } undo() { + final s = state; + if (s == null) { + return; + } final historyItem = undoStack.pop(); if (historyItem == null) { return; } - final transaction = historyItem.toTransaction(state!); - state!.apply(transaction, const ApplyOptions(noLog: true)); + final transaction = historyItem.toTransaction(s); + s.apply(transaction, const ApplyOptions(recordUndo: false)); } }