瀏覽代碼

fix: open-ai replace does not work in certain use-cases (#2100)

* test: added test to verify correct ordering after replacement of multiline text-nodes

* fix: open-ai replace does not work on certain use-cases

* refactor: using predefined operation insert node to create new nodes.

* Revert "refactor: using predefined operation insert node to create new nodes."

This reverts commit bcc014e84d09633ee14d5090f06e609fa95af481.

* refactor: using predefined operation insert node to create new nodes.

* fix: open-ai replace does not work in certain use-cases

* fix: fixed logic and tests for replacement of larger textNodes with smaller text.

---------

Co-authored-by: Lucas.Xu <[email protected]>
Mihir 2 年之前
父節點
當前提交
f9a1cb2623

+ 38 - 7
frontend/appflowy_flutter/packages/appflowy_editor/lib/src/core/transform/transaction.dart

@@ -363,6 +363,19 @@ extension TextTransaction on Transaction {
           );
         } else {
           deleteNode(textNode);
+          if (i == textNodes.length - 1) {
+            final delta = Delta()
+              ..insert(texts[0])
+              ..addAll(
+                textNodes.last.delta.slice(selection.end.offset),
+              );
+            replaceText(
+              textNode,
+              selection.start.offset,
+              texts[0].length,
+              delta.toPlainText(),
+            );
+          }
         }
       }
       afterSelection = null;
@@ -371,6 +384,8 @@ extension TextTransaction on Transaction {
 
     if (textNodes.length < texts.length) {
       final length = texts.length;
+      var path = textNodes.first.path;
+
       for (var i = 0; i < texts.length; i++) {
         final text = texts[i];
         if (i == 0) {
@@ -380,13 +395,15 @@ extension TextTransaction on Transaction {
             textNodes.first.toPlainText().length,
             text,
           );
-        } else if (i == length - 1) {
+          path = path.next;
+        } else if (i == length - 1 && textNodes.length >= 2) {
           replaceText(
             textNodes.last,
             0,
             selection.endIndex,
             text,
           );
+          path = path.next;
         } else {
           if (i < textNodes.length - 1) {
             replaceText(
@@ -395,14 +412,28 @@ extension TextTransaction on Transaction {
               textNodes[i].toPlainText().length,
               text,
             );
+            path = path.next;
           } else {
-            var path = textNodes.first.path;
-            var j = i - textNodes.length + length - 1;
-            while (j > 0) {
-              path = path.next;
-              j--;
+            if (i == texts.length - 1) {
+              final delta = Delta()
+                ..insert(text)
+                ..addAll(
+                  textNodes.last.delta.slice(selection.end.offset),
+                );
+              insertNode(
+                path,
+                TextNode(
+                  delta: delta,
+                ),
+              );
+            } else {
+              insertNode(
+                path,
+                TextNode(
+                  delta: Delta()..insert(text),
+                ),
+              );
             }
-            insertNode(path, TextNode(delta: Delta()..insert(text)));
           }
         }
       }

+ 38 - 1
frontend/appflowy_flutter/packages/appflowy_editor/test/core/transform/transaction_test.dart

@@ -125,7 +125,7 @@ void main() async {
           .map((e) => editor.nodeAtPath([e])!)
           .whereType<TextNode>()
           .toList(growable: false);
-      expect(textNodes[0].toPlainText(), '0123ABC');
+      expect(textNodes[0].toPlainText(), '0123ABC456789');
     });
 
     testWidgets('test replaceTexts, textNodes.length < texts.length',
@@ -165,5 +165,42 @@ void main() async {
       expect(textNodes[2].toPlainText(), 'ABC');
       expect(textNodes[3].toPlainText(), 'ABC456789');
     });
+
+    testWidgets('test replaceTexts, textNodes.length << texts.length',
+        (tester) async {
+      TestWidgetsFlutterBinding.ensureInitialized();
+
+      final editor = tester.editor..insertTextNode('Welcome to AppFlowy!');
+      await editor.startTesting();
+      await tester.pumpAndSettle();
+
+      expect(editor.documentLength, 1);
+
+      // select 'to'
+      final selection = Selection(
+        start: Position(path: [0], offset: 8),
+        end: Position(path: [0], offset: 10),
+      );
+      final transaction = editor.editorState.transaction;
+      var textNodes = [0]
+          .map((e) => editor.nodeAtPath([e])!)
+          .whereType<TextNode>()
+          .toList(growable: false);
+      final texts = ['ABC1', 'ABC2', 'ABC3', 'ABC4', 'ABC5'];
+      transaction.replaceTexts(textNodes, selection, texts);
+      editor.editorState.apply(transaction);
+      await tester.pumpAndSettle();
+
+      expect(editor.documentLength, 5);
+      textNodes = [0, 1, 2, 3, 4]
+          .map((e) => editor.nodeAtPath([e])!)
+          .whereType<TextNode>()
+          .toList(growable: false);
+      expect(textNodes[0].toPlainText(), 'Welcome ABC1');
+      expect(textNodes[1].toPlainText(), 'ABC2');
+      expect(textNodes[2].toPlainText(), 'ABC3');
+      expect(textNodes[3].toPlainText(), 'ABC4');
+      expect(textNodes[4].toPlainText(), 'ABC5 AppFlowy!');
+    });
   });
 }