浏览代码

feat: invert delta

Vincent Chan 2 年之前
父节点
当前提交
3cbac6f3f9

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

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

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

@@ -0,0 +1,23 @@
+typedef Attributes = Map<String, dynamic>;
+
+int hashAttributes(Attributes attributes) {
+  return Object.hashAllUnordered(
+      attributes.entries.map((e) => Object.hash(e.key, e.value)));
+}
+
+Attributes invertAttributes(Attributes? attr, Attributes? base) {
+  attr ??= {};
+  base ??= {};
+  final Attributes baseInverted = base.keys.fold({}, (memo, key) {
+    if (base![key] != attr![key] && attr.containsKey(key)) {
+      memo[key] = base[key];
+    }
+    return memo;
+  });
+  return attr.keys.fold(baseInverted, (memo, key) {
+    if (attr![key] != base![key] && base.containsKey(key)) {
+      memo[key] = null;
+    }
+    return memo;
+  });
+}

+ 1 - 2
frontend/app_flowy/packages/flowy_editor/lib/document/node.dart

@@ -1,8 +1,7 @@
 import 'dart:collection';
 import 'package:flowy_editor/document/path.dart';
 import 'package:flutter/material.dart';
-
-typedef Attributes = Map<String, dynamic>;
+import './attributes.dart';
 
 class Node extends ChangeNotifier with LinkedListEntry<Node> {
   Node? parent;

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

@@ -1,5 +1,6 @@
 import 'package:flowy_editor/document/node.dart';
 import 'package:flowy_editor/document/path.dart';
+import './attributes.dart';
 
 class StateTree {
   final Node root;

+ 29 - 11
frontend/app_flowy/packages/flowy_editor/lib/document/text_delta.dart

@@ -3,7 +3,7 @@ import 'dart:math';
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
-import './node.dart';
+import './attributes.dart';
 
 // constant number: 2^53 - 1
 const int _maxInt = 9007199254740991;
@@ -22,14 +22,6 @@ class TextOperation {
   }
 }
 
-int _hashAttributes(Attributes attributes) {
-  return Object.hashAllUnordered(
-    attributes.entries.map(
-      (e) => Object.hash(e.key, e.value),
-    ),
-  );
-}
-
 class TextInsert extends TextOperation {
   String content;
   final Attributes? _attributes;
@@ -60,7 +52,7 @@ class TextInsert extends TextOperation {
     final contentHash = content.hashCode;
     final attrs = _attributes;
     return Object.hash(
-        contentHash, attrs == null ? null : _hashAttributes(attrs));
+        contentHash, attrs == null ? null : hashAttributes(attrs));
   }
 }
 
@@ -104,7 +96,7 @@ class TextRetain extends TextOperation {
   @override
   int get hashCode {
     final attrs = _attributes;
-    return Object.hash(_length, attrs == null ? null : _hashAttributes(attrs));
+    return Object.hash(_length, attrs == null ? null : hashAttributes(attrs));
   }
 }
 
@@ -404,6 +396,32 @@ class Delta {
   int get hashCode {
     return hashList(operations);
   }
+
+  Delta invert(Delta base) {
+    final inverted = Delta();
+    operations.fold(0, (int previousValue, op) {
+      if (op is TextInsert) {
+        inverted.delete(op.length);
+      } else if (op is TextRetain && op.attributes == null) {
+        inverted.retain(op.length);
+        return previousValue + op.length;
+      } else if (op is TextDelete || op is TextRetain) {
+        final length = op.length;
+        final slice = base.slice(previousValue, previousValue + length);
+        for (final baseOp in slice.operations) {
+          if (op is TextDelete) {
+            inverted.add(baseOp);
+          } else if (op is TextRetain && op.attributes != null) {
+            inverted.retain(baseOp.length,
+                invertAttributes(op.attributes, baseOp.attributes));
+          }
+        }
+        return previousValue + length;
+      }
+      return previousValue;
+    });
+    return inverted.chop();
+  }
 }
 
 Attributes? _composeMap(Attributes? a, Attributes? b) {

+ 17 - 0
frontend/app_flowy/packages/flowy_editor/lib/operation/operation.dart

@@ -1,5 +1,7 @@
 import 'package:flowy_editor/document/path.dart';
 import 'package:flowy_editor/document/node.dart';
+import 'package:flowy_editor/document/text_delta.dart';
+import 'package:flowy_editor/document/attributes.dart';
 
 abstract class Operation {
   Operation invert();
@@ -61,3 +63,18 @@ class DeleteOperation extends Operation {
     );
   }
 }
+
+class TextEditOperation extends Operation {
+  final Path path;
+  final Delta delta;
+
+  TextEditOperation({
+    required this.path,
+    required this.delta,
+  });
+
+  @override
+  Operation invert() {
+    return TextEditOperation(path: path, delta: delta);
+  }
+}