From def03273b85065ba5cf51e6d7aa3fe684f25e7dd Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Wed, 13 Jul 2022 18:46:00 +0800 Subject: [PATCH] test: invert --- .../example/lib/plugin/image_node_widget.dart | 1 + .../flowy_editor/lib/document/attributes.dart | 19 + .../flowy_editor/lib/document/text_delta.dart | 22 +- .../flowy_editor/lib/editor_state.dart | 1 + .../flowy_editor/test/delta_test.dart | 339 ++++++++++-------- 5 files changed, 206 insertions(+), 176 deletions(-) diff --git a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart b/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart index 11607d7bd6..33425eccd5 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart +++ b/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({ diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart index ea7106ab0a..6e845420ef 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/document/attributes.dart +++ b/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; +} diff --git a/frontend/app_flowy/packages/flowy_editor/lib/document/text_delta.dart b/frontend/app_flowy/packages/flowy_editor/lib/document/text_delta.dart index 2d3f8301f5..bbf8e20a68 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/document/text_delta.dart +++ b/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; -} diff --git a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart index d60c06a497..30ffb68009 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart +++ b/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'; diff --git a/frontend/app_flowy/packages/flowy_editor/test/delta_test.dart b/frontend/app_flowy/packages/flowy_editor/test/delta_test.dart index 3ddd01efb7..bce2517744 100644 --- a/frontend/app_flowy/packages/flowy_editor/test/delta_test.dart +++ b/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([ - TextInsert('Gandalf', { + group('compose', () { + test('test delta', () { + final delta = Delta([ + TextInsert('Gandalf', { + 'bold': true, + }), + TextInsert(' the '), + TextInsert('Grey', { + 'color': '#ccc', + }) + ]); + + final death = Delta().retain(12).insert("White", { + 'color': '#fff', + }).delete(4); + + final restores = delta.compose(death); + expect(restores.operations, [ + 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, - }), - TextInsert(' the '), - TextInsert('Grey', { - 'color': '#ccc', - }) - ]); - - final death = Delta().retain(12).insert("White", { - 'color': '#fff', - }).delete(4); - - final restores = delta.compose(death); - expect(restores.operations, [ - 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', + 'color': 'red', + }); + final expected = Delta().insert('A', { + 'bold': true, + 'color': 'red', + }); + expect(a.compose(b), expected); }); - final expected = Delta().insert('A', { - '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('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('delete + insert', () { + final a = Delta().delete(1); + final b = Delta().insert('B'); + final expected = Delta().insert('B').delete(1); + expect(a.compose(b), expected); }); - final expected = Delta().delete(1).retain(1, { - 'bold': true, - 'color': 'red', + 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); }); - 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 + delete', () { + final a = Delta().delete(1); + final b = Delta().delete(1); + final expected = Delta().delete(2); + expect(a.compose(b), expected); }); - expect(a.compose(b), expected); - }); - test('retain + retain', () { - final a = Delta().retain(1, { - 'color': 'blue', + 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); }); - final b = Delta().retain(1, { - 'bold': true, - 'color': 'red', + 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); }); - final expected = Delta().retain(1, { - 'bold': true, - 'color': 'red', + 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); }); - expect(a.compose(b), expected); - }); - test('retain + delete', () { - final a = Delta().retain(1, { - 'color': 'blue', + 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); + // }); }); }