|  | @@ -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;
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 |