operation.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import 'package:flowy_editor/document/attributes.dart';
  2. import 'package:flowy_editor/flowy_editor.dart';
  3. abstract class Operation {
  4. final Path path;
  5. Operation({required this.path});
  6. Operation copyWithPath(Path path);
  7. Operation invert();
  8. }
  9. class InsertOperation extends Operation {
  10. final Node value;
  11. InsertOperation({
  12. required super.path,
  13. required this.value,
  14. });
  15. InsertOperation copyWith({Path? path, Node? value}) =>
  16. InsertOperation(path: path ?? this.path, value: value ?? this.value);
  17. @override
  18. Operation copyWithPath(Path path) => copyWith(path: path);
  19. @override
  20. Operation invert() {
  21. return DeleteOperation(
  22. path: path,
  23. removedValue: value,
  24. );
  25. }
  26. }
  27. class UpdateOperation extends Operation {
  28. final Attributes attributes;
  29. final Attributes oldAttributes;
  30. UpdateOperation({
  31. required super.path,
  32. required this.attributes,
  33. required this.oldAttributes,
  34. });
  35. UpdateOperation copyWith(
  36. {Path? path, Attributes? attributes, Attributes? oldAttributes}) =>
  37. UpdateOperation(
  38. path: path ?? this.path,
  39. attributes: attributes ?? this.attributes,
  40. oldAttributes: oldAttributes ?? this.oldAttributes);
  41. @override
  42. Operation copyWithPath(Path path) => copyWith(path: path);
  43. @override
  44. Operation invert() {
  45. return UpdateOperation(
  46. path: path,
  47. attributes: oldAttributes,
  48. oldAttributes: attributes,
  49. );
  50. }
  51. }
  52. class DeleteOperation extends Operation {
  53. final Node removedValue;
  54. DeleteOperation({
  55. required super.path,
  56. required this.removedValue,
  57. });
  58. DeleteOperation copyWith({Path? path, Node? removedValue}) => DeleteOperation(
  59. path: path ?? this.path, removedValue: removedValue ?? this.removedValue);
  60. @override
  61. Operation copyWithPath(Path path) => copyWith(path: path);
  62. @override
  63. Operation invert() {
  64. return InsertOperation(
  65. path: path,
  66. value: removedValue,
  67. );
  68. }
  69. }
  70. class TextEditOperation extends Operation {
  71. final Delta delta;
  72. final Delta inverted;
  73. TextEditOperation({
  74. required super.path,
  75. required this.delta,
  76. required this.inverted,
  77. });
  78. TextEditOperation copyWith({Path? path, Delta? delta, Delta? inverted}) =>
  79. TextEditOperation(
  80. path: path ?? this.path,
  81. delta: delta ?? this.delta,
  82. inverted: inverted ?? this.inverted);
  83. @override
  84. Operation copyWithPath(Path path) => copyWith(path: path);
  85. @override
  86. Operation invert() {
  87. return TextEditOperation(path: path, delta: inverted, inverted: delta);
  88. }
  89. }
  90. Path transformPath(Path preInsertPath, Path b, [int delta = 1]) {
  91. if (preInsertPath.length > b.length) {
  92. return b;
  93. }
  94. if (preInsertPath.isEmpty || b.isEmpty) {
  95. return b;
  96. }
  97. // check the prefix
  98. for (var i = 0; i < preInsertPath.length - 1; i++) {
  99. if (preInsertPath[i] != b[i]) {
  100. return b;
  101. }
  102. }
  103. final prefix = preInsertPath.sublist(0, preInsertPath.length - 1);
  104. final suffix = b.sublist(preInsertPath.length);
  105. final preInsertLast = preInsertPath.last;
  106. final bAtIndex = b[preInsertPath.length - 1];
  107. if (preInsertLast <= bAtIndex) {
  108. prefix.add(bAtIndex + delta);
  109. }
  110. prefix.addAll(suffix);
  111. return prefix;
  112. }
  113. Operation transformOperation(Operation a, Operation b) {
  114. if (a is InsertOperation) {
  115. final newPath = transformPath(a.path, b.path);
  116. return b.copyWithPath(newPath);
  117. } else if (b is DeleteOperation) {
  118. final newPath = transformPath(a.path, b.path, -1);
  119. return b.copyWithPath(newPath);
  120. }
  121. // TODO: transform update and textedit
  122. return b;
  123. }