|
@@ -1,218 +0,0 @@
|
|
|
-import 'package:appflowy_editor/appflowy_editor.dart';
|
|
|
-
|
|
|
-abstract class Operation {
|
|
|
- factory Operation.fromJson(Map<String, dynamic> map) {
|
|
|
- String t = map["op"] as String;
|
|
|
- if (t == "insert") {
|
|
|
- return InsertOperation.fromJson(map);
|
|
|
- } else if (t == "update") {
|
|
|
- return UpdateOperation.fromJson(map);
|
|
|
- } else if (t == "delete") {
|
|
|
- return DeleteOperation.fromJson(map);
|
|
|
- } else if (t == "text-edit") {
|
|
|
- return TextEditOperation.fromJson(map);
|
|
|
- }
|
|
|
-
|
|
|
- throw ArgumentError('unexpected type $t');
|
|
|
- }
|
|
|
- final Path path;
|
|
|
- Operation(this.path);
|
|
|
- Operation copyWithPath(Path path);
|
|
|
- Operation invert();
|
|
|
- Map<String, dynamic> toJson();
|
|
|
-}
|
|
|
-
|
|
|
-class InsertOperation extends Operation {
|
|
|
- final List<Node> nodes;
|
|
|
-
|
|
|
- factory InsertOperation.fromJson(Map<String, dynamic> map) {
|
|
|
- final path = map["path"] as List<int>;
|
|
|
- final value =
|
|
|
- (map["nodes"] as List<dynamic>).map((n) => Node.fromJson(n)).toList();
|
|
|
- return InsertOperation(path, value);
|
|
|
- }
|
|
|
-
|
|
|
- InsertOperation(Path path, this.nodes) : super(path);
|
|
|
-
|
|
|
- InsertOperation copyWith({Path? path, List<Node>? nodes}) =>
|
|
|
- InsertOperation(path ?? this.path, nodes ?? this.nodes);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation copyWithPath(Path path) => copyWith(path: path);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation invert() {
|
|
|
- return DeleteOperation(
|
|
|
- path,
|
|
|
- nodes,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- Map<String, dynamic> toJson() {
|
|
|
- return {
|
|
|
- "op": "insert",
|
|
|
- "path": path.toList(),
|
|
|
- "nodes": nodes.map((n) => n.toJson()),
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class UpdateOperation extends Operation {
|
|
|
- final Attributes attributes;
|
|
|
- final Attributes oldAttributes;
|
|
|
-
|
|
|
- factory UpdateOperation.fromJson(Map<String, dynamic> map) {
|
|
|
- final path = map["path"] as List<int>;
|
|
|
- final attributes = map["attributes"] as Map<String, dynamic>;
|
|
|
- final oldAttributes = map["oldAttributes"] as Map<String, dynamic>;
|
|
|
- return UpdateOperation(path, attributes, oldAttributes);
|
|
|
- }
|
|
|
-
|
|
|
- UpdateOperation(
|
|
|
- Path path,
|
|
|
- this.attributes,
|
|
|
- this.oldAttributes,
|
|
|
- ) : super(path);
|
|
|
-
|
|
|
- UpdateOperation copyWith(
|
|
|
- {Path? path, Attributes? attributes, Attributes? oldAttributes}) =>
|
|
|
- UpdateOperation(path ?? this.path, attributes ?? this.attributes,
|
|
|
- oldAttributes ?? this.oldAttributes);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation copyWithPath(Path path) => copyWith(path: path);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation invert() {
|
|
|
- return UpdateOperation(
|
|
|
- path,
|
|
|
- oldAttributes,
|
|
|
- attributes,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- Map<String, dynamic> toJson() {
|
|
|
- return {
|
|
|
- "op": "update",
|
|
|
- "path": path.toList(),
|
|
|
- "attributes": {...attributes},
|
|
|
- "oldAttributes": {...oldAttributes},
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class DeleteOperation extends Operation {
|
|
|
- final List<Node> nodes;
|
|
|
-
|
|
|
- factory DeleteOperation.fromJson(Map<String, dynamic> map) {
|
|
|
- final path = map["path"] as List<int>;
|
|
|
- final List<Node> nodes =
|
|
|
- (map["nodes"] as List<dynamic>).map((e) => Node.fromJson(e)).toList();
|
|
|
- return DeleteOperation(path, nodes);
|
|
|
- }
|
|
|
-
|
|
|
- DeleteOperation(
|
|
|
- Path path,
|
|
|
- this.nodes,
|
|
|
- ) : super(path);
|
|
|
-
|
|
|
- DeleteOperation copyWith({Path? path, List<Node>? nodes}) =>
|
|
|
- DeleteOperation(path ?? this.path, nodes ?? this.nodes);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation copyWithPath(Path path) => copyWith(path: path);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation invert() {
|
|
|
- return InsertOperation(path, nodes);
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- Map<String, dynamic> toJson() {
|
|
|
- return {
|
|
|
- "op": "delete",
|
|
|
- "path": path.toList(),
|
|
|
- "nodes": nodes.map((n) => n.toJson()),
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class TextEditOperation extends Operation {
|
|
|
- final Delta delta;
|
|
|
- final Delta inverted;
|
|
|
-
|
|
|
- factory TextEditOperation.fromJson(Map<String, dynamic> map) {
|
|
|
- final path = map["path"] as List<int>;
|
|
|
- final delta = Delta.fromJson(map["delta"]);
|
|
|
- final invert = Delta.fromJson(map["invert"]);
|
|
|
- return TextEditOperation(path, delta, invert);
|
|
|
- }
|
|
|
-
|
|
|
- TextEditOperation(
|
|
|
- Path path,
|
|
|
- this.delta,
|
|
|
- this.inverted,
|
|
|
- ) : super(path);
|
|
|
-
|
|
|
- TextEditOperation copyWith({Path? path, Delta? delta, Delta? inverted}) =>
|
|
|
- TextEditOperation(
|
|
|
- path ?? this.path, delta ?? this.delta, inverted ?? this.inverted);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation copyWithPath(Path path) => copyWith(path: path);
|
|
|
-
|
|
|
- @override
|
|
|
- Operation invert() {
|
|
|
- return TextEditOperation(path, inverted, delta);
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- Map<String, dynamic> toJson() {
|
|
|
- return {
|
|
|
- "op": "text-edit",
|
|
|
- "path": path.toList(),
|
|
|
- "delta": delta.toJson(),
|
|
|
- "invert": inverted.toJson(),
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-Path transformPath(Path preInsertPath, Path b, [int delta = 1]) {
|
|
|
- if (preInsertPath.length > b.length) {
|
|
|
- return b;
|
|
|
- }
|
|
|
- if (preInsertPath.isEmpty || b.isEmpty) {
|
|
|
- return b;
|
|
|
- }
|
|
|
- // check the prefix
|
|
|
- for (var i = 0; i < preInsertPath.length - 1; i++) {
|
|
|
- if (preInsertPath[i] != b[i]) {
|
|
|
- return b;
|
|
|
- }
|
|
|
- }
|
|
|
- final prefix = preInsertPath.sublist(0, preInsertPath.length - 1);
|
|
|
- final suffix = b.sublist(preInsertPath.length);
|
|
|
- final preInsertLast = preInsertPath.last;
|
|
|
- final bAtIndex = b[preInsertPath.length - 1];
|
|
|
- if (preInsertLast <= bAtIndex) {
|
|
|
- prefix.add(bAtIndex + delta);
|
|
|
- } else {
|
|
|
- prefix.add(bAtIndex);
|
|
|
- }
|
|
|
- prefix.addAll(suffix);
|
|
|
- return prefix;
|
|
|
-}
|
|
|
-
|
|
|
-Operation transformOperation(Operation a, Operation b) {
|
|
|
- if (a is InsertOperation) {
|
|
|
- final newPath = transformPath(a.path, b.path, a.nodes.length);
|
|
|
- return b.copyWithPath(newPath);
|
|
|
- } else if (a is DeleteOperation) {
|
|
|
- final newPath = transformPath(a.path, b.path, -1 * a.nodes.length);
|
|
|
- return b.copyWithPath(newPath);
|
|
|
- }
|
|
|
- // TODO: transform update and textedit
|
|
|
- return b;
|
|
|
-}
|