Vincent Chan 2 vuotta sitten
vanhempi
commit
def03273b8

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart

@@ -1,6 +1,7 @@
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
+import 'package:flowy_editor/document/attributes.dart';
 
 class ImageNodeBuilder extends NodeWidgetBuilder {
   ImageNodeBuilder.create({

+ 19 - 0
frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart

@@ -21,3 +21,22 @@ Attributes invertAttributes(Attributes? attr, Attributes? base) {
     return memo;
   });
 }
+
+Attributes? composeAttributes(Attributes? a, Attributes? b) {
+  a ??= {};
+  b ??= {};
+  final Attributes attributes = {};
+  attributes.addAll(b);
+
+  for (final entry in a.entries) {
+    if (!b.containsKey(entry.key)) {
+      attributes[entry.key] = entry.value;
+    }
+  }
+
+  if (attributes.isEmpty) {
+    return null;
+  }
+
+  return attributes;
+}

+ 2 - 20
frontend/app_flowy/packages/flowy_editor/lib/document/text_delta.dart

@@ -336,7 +336,8 @@ class Delta {
         final length = min(thisIter.peekLength(), otherIter.peekLength());
         final thisOp = thisIter.next(length);
         final otherOp = otherIter.next(length);
-        final attributes = _composeMap(thisOp.attributes, otherOp.attributes);
+        final attributes =
+            composeAttributes(thisOp.attributes, otherOp.attributes);
         if (otherOp is TextRetain && otherOp.length > 0) {
           TextOperation? newOp;
           if (thisOp is TextRetain) {
@@ -423,22 +424,3 @@ class Delta {
     return inverted.chop();
   }
 }
-
-Attributes? _composeMap(Attributes? a, Attributes? b) {
-  a ??= {};
-  b ??= {};
-  final Attributes attributes = {};
-  attributes.addAll(b);
-
-  for (final entry in a.entries) {
-    if (!b.containsKey(entry.key)) {
-      attributes[entry.key] = entry.value;
-    }
-  }
-
-  if (attributes.isEmpty) {
-    return null;
-  }
-
-  return attributes;
-}

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart

@@ -1,5 +1,6 @@
 import 'package:flowy_editor/document/node.dart';
 import 'package:flowy_editor/operation/operation.dart';
+import 'package:flowy_editor/document/attributes.dart';
 import 'package:flutter/material.dart';
 
 import './document/state_tree.dart';

+ 182 - 155
frontend/app_flowy/packages/flowy_editor/test/delta_test.dart

@@ -2,172 +2,199 @@ import 'package:flutter_test/flutter_test.dart';
 import 'package:flowy_editor/document/text_delta.dart';
 
 void main() {
-  TestWidgetsFlutterBinding.ensureInitialized();
-  test('test delta', () {
-    final delta = Delta(<TextOperation>[
-      TextInsert('Gandalf', {
-        'bold': true,
-      }),
-      TextInsert(' the '),
-      TextInsert('Grey', {
-        'color': '#ccc',
-      })
-    ]);
+  group('compose', () {
+    test('test delta', () {
+      final delta = Delta(<TextOperation>[
+        TextInsert('Gandalf', {
+          'bold': true,
+        }),
+        TextInsert(' the '),
+        TextInsert('Grey', {
+          'color': '#ccc',
+        })
+      ]);
 
-    final death = Delta().retain(12).insert("White", {
-      'color': '#fff',
-    }).delete(4);
+      final death = Delta().retain(12).insert("White", {
+        'color': '#fff',
+      }).delete(4);
 
-    final restores = delta.compose(death);
-    expect(restores.operations, <TextOperation>[
-      TextInsert('Gandalf', {'bold': true}),
-      TextInsert(' the '),
-      TextInsert('White', {'color': '#fff'}),
-    ]);
-  });
-  test('compose()', () {
-    final a = Delta().insert('A');
-    final b = Delta().insert('B');
-    final expected = Delta().insert('B').insert('A');
-    expect(a.compose(b), expected);
-  });
-  test('insert + retain', () {
-    final a = Delta().insert('A');
-    final b = Delta().retain(1, {
-      'bold': true,
-      'color': 'red',
+      final restores = delta.compose(death);
+      expect(restores.operations, <TextOperation>[
+        TextInsert('Gandalf', {'bold': true}),
+        TextInsert(' the '),
+        TextInsert('White', {'color': '#fff'}),
+      ]);
     });
-    final expected = Delta().insert('A', {
-      'bold': true,
-      'color': 'red',
+    test('compose()', () {
+      final a = Delta().insert('A');
+      final b = Delta().insert('B');
+      final expected = Delta().insert('B').insert('A');
+      expect(a.compose(b), expected);
     });
-    expect(a.compose(b), expected);
-  });
-  test('insert + delete', () {
-    final a = Delta().insert('A');
-    final b = Delta().delete(1);
-    final expected = Delta();
-    expect(a.compose(b), expected);
-  });
-  test('delete + insert', () {
-    final a = Delta().delete(1);
-    final b = Delta().insert('B');
-    final expected = Delta().insert('B').delete(1);
-    expect(a.compose(b), expected);
-  });
-  test('delete + retain', () {
-    final a = Delta().delete(1);
-    final b = Delta().retain(1, {
-      'bold': true,
-      'color': 'red',
+    test('insert + retain', () {
+      final a = Delta().insert('A');
+      final b = Delta().retain(1, {
+        'bold': true,
+        'color': 'red',
+      });
+      final expected = Delta().insert('A', {
+        'bold': true,
+        'color': 'red',
+      });
+      expect(a.compose(b), expected);
     });
-    final expected = Delta().delete(1).retain(1, {
-      'bold': true,
-      'color': 'red',
+    test('insert + delete', () {
+      final a = Delta().insert('A');
+      final b = Delta().delete(1);
+      final expected = Delta();
+      expect(a.compose(b), expected);
     });
-    expect(a.compose(b), expected);
-  });
-  test('delete + delete', () {
-    final a = Delta().delete(1);
-    final b = Delta().delete(1);
-    final expected = Delta().delete(2);
-    expect(a.compose(b), expected);
-  });
-  test('retain + insert', () {
-    final a = Delta().retain(1, {'color': 'blue'});
-    final b = Delta().insert('B');
-    final expected = Delta().insert('B').retain(1, {
-      'color': 'blue',
+    test('delete + insert', () {
+      final a = Delta().delete(1);
+      final b = Delta().insert('B');
+      final expected = Delta().insert('B').delete(1);
+      expect(a.compose(b), expected);
     });
-    expect(a.compose(b), expected);
-  });
-  test('retain + retain', () {
-    final a = Delta().retain(1, {
-      'color': 'blue',
+    test('delete + retain', () {
+      final a = Delta().delete(1);
+      final b = Delta().retain(1, {
+        'bold': true,
+        'color': 'red',
+      });
+      final expected = Delta().delete(1).retain(1, {
+        'bold': true,
+        'color': 'red',
+      });
+      expect(a.compose(b), expected);
     });
-    final b = Delta().retain(1, {
-      'bold': true,
-      'color': 'red',
+    test('delete + delete', () {
+      final a = Delta().delete(1);
+      final b = Delta().delete(1);
+      final expected = Delta().delete(2);
+      expect(a.compose(b), expected);
     });
-    final expected = Delta().retain(1, {
-      'bold': true,
-      'color': 'red',
+    test('retain + insert', () {
+      final a = Delta().retain(1, {'color': 'blue'});
+      final b = Delta().insert('B');
+      final expected = Delta().insert('B').retain(1, {
+        'color': 'blue',
+      });
+      expect(a.compose(b), expected);
     });
-    expect(a.compose(b), expected);
-  });
-  test('retain + delete', () {
-    final a = Delta().retain(1, {
-      'color': 'blue',
+    test('retain + retain', () {
+      final a = Delta().retain(1, {
+        'color': 'blue',
+      });
+      final b = Delta().retain(1, {
+        'bold': true,
+        'color': 'red',
+      });
+      final expected = Delta().retain(1, {
+        'bold': true,
+        'color': 'red',
+      });
+      expect(a.compose(b), expected);
+    });
+    test('retain + delete', () {
+      final a = Delta().retain(1, {
+        'color': 'blue',
+      });
+      final b = Delta().delete(1);
+      final expected = Delta().delete(1);
+      expect(a.compose(b), expected);
+    });
+    test('insert in middle of text', () {
+      final a = Delta().insert('Hello');
+      final b = Delta().retain(3).insert('X');
+      final expected = Delta().insert('HelXlo');
+      expect(a.compose(b), expected);
+    });
+    test('insert and delete ordering', () {
+      final a = Delta().insert('Hello');
+      final b = Delta().insert('Hello');
+      final insertFirst = Delta().retain(3).insert('X').delete(1);
+      final deleteFirst = Delta().retain(3).delete(1).insert('X');
+      final expected = Delta().insert('HelXo');
+      expect(a.compose(insertFirst), expected);
+      expect(b.compose(deleteFirst), expected);
+    });
+    test('delete entire text', () {
+      final a = Delta().retain(4).insert('Hello');
+      final b = Delta().delete(9);
+      final expected = Delta().delete(4);
+      expect(a.compose(b), expected);
+    });
+    test('retain more than length of text', () {
+      final a = Delta().insert('Hello');
+      final b = Delta().retain(10);
+      final expected = Delta().insert('Hello');
+      expect(a.compose(b), expected);
+    });
+    test('retain start optimization', () {
+      final a = Delta()
+          .insert('A', {'bold': true})
+          .insert('B')
+          .insert('C', {'bold': true})
+          .delete(1);
+      final b = Delta().retain(3).insert('D');
+      final expected = Delta()
+          .insert('A', {'bold': true})
+          .insert('B')
+          .insert('C', {'bold': true})
+          .insert('D')
+          .delete(1);
+      expect(a.compose(b), expected);
+    });
+    test('retain end optimization', () {
+      final a = Delta()
+          .insert('A', {'bold': true})
+          .insert('B')
+          .insert('C', {'bold': true});
+      final b = Delta().delete(1);
+      final expected = Delta().insert('B').insert('C', {'bold': true});
+      expect(a.compose(b), expected);
+    });
+    test('retain end optimization join', () {
+      final a = Delta()
+          .insert('A', {'bold': true})
+          .insert('B')
+          .insert('C', {'bold': true})
+          .insert('D')
+          .insert('E', {'bold': true})
+          .insert('F');
+      final b = Delta().retain(1).delete(1);
+      final expected = Delta()
+          .insert('AC', {'bold': true})
+          .insert('D')
+          .insert('E', {'bold': true})
+          .insert('F');
+      expect(a.compose(b), expected);
     });
-    final b = Delta().delete(1);
-    final expected = Delta().delete(1);
-    expect(a.compose(b), expected);
-  });
-  test('insert in middle of text', () {
-    final a = Delta().insert('Hello');
-    final b = Delta().retain(3).insert('X');
-    final expected = Delta().insert('HelXlo');
-    expect(a.compose(b), expected);
-  });
-  test('insert and delete ordering', () {
-    final a = Delta().insert('Hello');
-    final b = Delta().insert('Hello');
-    final insertFirst = Delta().retain(3).insert('X').delete(1);
-    final deleteFirst = Delta().retain(3).delete(1).insert('X');
-    final expected = Delta().insert('HelXo');
-    expect(a.compose(insertFirst), expected);
-    expect(b.compose(deleteFirst), expected);
-  });
-  test('delete entire text', () {
-    final a = Delta().retain(4).insert('Hello');
-    final b = Delta().delete(9);
-    final expected = Delta().delete(4);
-    expect(a.compose(b), expected);
-  });
-  test('retain more than length of text', () {
-    final a = Delta().insert('Hello');
-    final b = Delta().retain(10);
-    final expected = Delta().insert('Hello');
-    expect(a.compose(b), expected);
-  });
-  test('retain start optimization', () {
-    final a = Delta()
-        .insert('A', {'bold': true})
-        .insert('B')
-        .insert('C', {'bold': true})
-        .delete(1);
-    final b = Delta().retain(3).insert('D');
-    final expected = Delta()
-        .insert('A', {'bold': true})
-        .insert('B')
-        .insert('C', {'bold': true})
-        .insert('D')
-        .delete(1);
-    expect(a.compose(b), expected);
-  });
-  test('retain end optimization', () {
-    final a = Delta()
-        .insert('A', {'bold': true})
-        .insert('B')
-        .insert('C', {'bold': true});
-    final b = Delta().delete(1);
-    final expected = Delta().insert('B').insert('C', {'bold': true});
-    expect(a.compose(b), expected);
   });
-  test('retain end optimization join', () {
-    final a = Delta()
-        .insert('A', {'bold': true})
-        .insert('B')
-        .insert('C', {'bold': true})
-        .insert('D')
-        .insert('E', {'bold': true})
-        .insert('F');
-    final b = Delta().retain(1).delete(1);
-    final expected = Delta()
-        .insert('AC', {'bold': true})
-        .insert('D')
-        .insert('E', {'bold': true})
-        .insert('F');
-    expect(a.compose(b), expected);
+  group('invert', () {
+    test('insert', () {
+      final delta = Delta().retain(2).insert('A');
+      final base = Delta().insert('12346');
+      final expected = Delta().retain(2).delete(1);
+      final inverted = delta.invert(base);
+      expect(expected, inverted);
+      expect(base.compose(delta).compose(inverted), base);
+    });
+    test('delete', () {
+      final delta = Delta().retain(2).delete(3);
+      final base = Delta().insert('123456');
+      final expected = Delta().retain(2).insert('345');
+      final inverted = delta.invert(base);
+      expect(expected, inverted);
+      expect(base.compose(delta).compose(inverted), base);
+    });
+    // test('retain', () {
+    //   final delta = Delta().retain(2).retain(3, {'bold': true});
+    //   final base = Delta().insert('123456');
+    //   final expected = Delta().retain(2).retain(3, {'bold': null});
+    //   final inverted = delta.invert(base);
+    //   expect(expected, inverted);
+    //   expect(base.compose(delta).compose(inverted), base);
+    // });
   });
 }