Selaa lähdekoodia

Merge pull request #1554 from LucasXu0/rules

feat: add rules for editorstate.apply
Lucas.Xu 2 vuotta sitten
vanhempi
commit
8494e0f0a6

+ 37 - 5
frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart

@@ -1,4 +1,5 @@
 import 'dart:async';
+import 'package:appflowy_editor/src/core/document/node.dart';
 import 'package:appflowy_editor/src/infra/log.dart';
 import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart';
 import 'package:appflowy_editor/src/render/style/editor_style.dart';
@@ -72,6 +73,7 @@ class EditorState {
 
   // TODO: only for testing.
   bool disableSealTimer = false;
+  bool disbaleRules = false;
 
   bool editable = true;
 
@@ -119,8 +121,12 @@ class EditorState {
   ///
   /// The options can be used to determine whether the editor
   /// should record the transaction in undo/redo stack.
-  apply(Transaction transaction,
-      [ApplyOptions options = const ApplyOptions(recordUndo: true)]) {
+  void apply(
+    Transaction transaction, {
+    ApplyOptions options = const ApplyOptions(recordUndo: true),
+    ruleCount = 0,
+    withUpdateCursor = true,
+  }) {
     if (!editable) {
       return;
     }
@@ -132,7 +138,10 @@ class EditorState {
     _observer.add(transaction);
 
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      updateCursorSelection(transaction.afterSelection);
+      _applyRules(ruleCount);
+      if (withUpdateCursor) {
+        updateCursorSelection(transaction.afterSelection);
+      }
     });
 
     if (options.recordUndo) {
@@ -153,7 +162,7 @@ class EditorState {
     }
   }
 
-  _debouncedSealHistoryItem() {
+  void _debouncedSealHistoryItem() {
     if (disableSealTimer) {
       return;
     }
@@ -168,7 +177,7 @@ class EditorState {
     });
   }
 
-  _applyOperation(Operation op) {
+  void _applyOperation(Operation op) {
     if (op is InsertOperation) {
       document.insert(op.path, op.nodes);
     } else if (op is UpdateOperation) {
@@ -179,4 +188,27 @@ class EditorState {
       document.updateText(op.path, op.delta);
     }
   }
+
+  void _applyRules(int ruleCount) {
+    // Set a maximum count to prevent a dead loop.
+    if (ruleCount >= 5 || disbaleRules) {
+      return;
+    }
+
+    final tr = transaction;
+
+    // Rules
+    _insureLastNodeEditable(tr);
+
+    if (tr.operations.isNotEmpty) {
+      apply(tr, ruleCount: ruleCount + 1, withUpdateCursor: false);
+    }
+  }
+
+  void _insureLastNodeEditable(Transaction tr) {
+    if (document.root.children.isEmpty ||
+        document.root.children.last.id != 'text') {
+      tr.insertNode([document.root.children.length], TextNode.empty());
+    }
+  }
 }

+ 2 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/history/undo_manager.dart

@@ -123,7 +123,7 @@ class UndoManager {
     final transaction = historyItem.toTransaction(s);
     s.apply(
       transaction,
-      const ApplyOptions(
+      options: const ApplyOptions(
         recordUndo: false,
         recordRedo: true,
       ),
@@ -143,7 +143,7 @@ class UndoManager {
     final transaction = historyItem.toTransaction(s);
     s.apply(
       transaction,
-      const ApplyOptions(
+      options: const ApplyOptions(
         recordUndo: true,
         recordRedo: false,
       ),

+ 3 - 1
frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart

@@ -157,7 +157,9 @@ class EditorWidgetTester {
       document: Document(
         root: _createEmptyEditorRoot(),
       ),
-    )..disableSealTimer = true;
+    )
+      ..disableSealTimer = true
+      ..disbaleRules = true;
   }
 }
 

+ 2 - 1
frontend/app_flowy/packages/appflowy_editor_plugins/lib/src/code_block/code_block_shortcut_event.dart

@@ -97,7 +97,8 @@ SelectionMenuItem codeBlockMenuItem = SelectionMenuItem(
       return;
     }
     final transaction = editorState.transaction;
-    if (textNodes.first.toPlainText().isEmpty) {
+    final textNode = textNodes.first;
+    if (textNode.toPlainText().isEmpty && textNode.next is TextNode) {
       transaction.updateNode(textNodes.first, {
         BuiltInAttributeKey.subtype: kCodeBlockSubType,
         kCodeBlockAttrTheme: 'vs',