test: invert

This commit is contained in:
Vincent Chan 2022-07-13 18:46:00 +08:00
parent 3cbac6f3f9
commit def03273b8
5 changed files with 206 additions and 176 deletions

View File

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

View File

@ -21,3 +21,22 @@ Attributes invertAttributes(Attributes? attr, Attributes? base) {
return memo; 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;
}

View File

@ -336,7 +336,8 @@ class Delta {
final length = min(thisIter.peekLength(), otherIter.peekLength()); final length = min(thisIter.peekLength(), otherIter.peekLength());
final thisOp = thisIter.next(length); final thisOp = thisIter.next(length);
final otherOp = otherIter.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) { if (otherOp is TextRetain && otherOp.length > 0) {
TextOperation? newOp; TextOperation? newOp;
if (thisOp is TextRetain) { if (thisOp is TextRetain) {
@ -423,22 +424,3 @@ class Delta {
return inverted.chop(); 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;
}

View File

@ -1,5 +1,6 @@
import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/operation/operation.dart'; import 'package:flowy_editor/operation/operation.dart';
import 'package:flowy_editor/document/attributes.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import './document/state_tree.dart'; import './document/state_tree.dart';

View File

@ -2,172 +2,199 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flowy_editor/document/text_delta.dart'; import 'package:flowy_editor/document/text_delta.dart';
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); group('compose', () {
test('test delta', () { test('test delta', () {
final delta = Delta(<TextOperation>[ final delta = Delta(<TextOperation>[
TextInsert('Gandalf', { 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, <TextOperation>[
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, 'bold': true,
}), 'color': 'red',
TextInsert(' the '), });
TextInsert('Grey', { final expected = Delta().insert('A', {
'color': '#ccc', 'bold': true,
}) 'color': 'red',
]); });
expect(a.compose(b), expected);
final death = Delta().retain(12).insert("White", {
'color': '#fff',
}).delete(4);
final restores = delta.compose(death);
expect(restores.operations, <TextOperation>[
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',
}); });
final expected = Delta().insert('A', { test('insert + delete', () {
'bold': true, final a = Delta().insert('A');
'color': 'red', final b = Delta().delete(1);
final expected = Delta();
expect(a.compose(b), expected);
}); });
expect(a.compose(b), expected); test('delete + insert', () {
}); final a = Delta().delete(1);
test('insert + delete', () { final b = Delta().insert('B');
final a = Delta().insert('A'); final expected = Delta().insert('B').delete(1);
final b = Delta().delete(1); expect(a.compose(b), expected);
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',
}); });
final expected = Delta().delete(1).retain(1, { test('delete + retain', () {
'bold': true, final a = Delta().delete(1);
'color': 'red', 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);
test('delete + delete', () { final b = Delta().delete(1);
final a = Delta().delete(1); final expected = Delta().delete(2);
final b = Delta().delete(1); expect(a.compose(b), expected);
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',
}); });
expect(a.compose(b), expected); test('retain + insert', () {
}); final a = Delta().retain(1, {'color': 'blue'});
test('retain + retain', () { final b = Delta().insert('B');
final a = Delta().retain(1, { final expected = Delta().insert('B').retain(1, {
'color': 'blue', 'color': 'blue',
});
expect(a.compose(b), expected);
}); });
final b = Delta().retain(1, { test('retain + retain', () {
'bold': true, final a = Delta().retain(1, {
'color': 'red', '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, { test('retain + delete', () {
'bold': true, final a = Delta().retain(1, {
'color': 'red', 'color': 'blue',
});
final b = Delta().delete(1);
final expected = Delta().delete(1);
expect(a.compose(b), expected);
}); });
expect(a.compose(b), expected); test('insert in middle of text', () {
}); final a = Delta().insert('Hello');
test('retain + delete', () { final b = Delta().retain(3).insert('X');
final a = Delta().retain(1, { final expected = Delta().insert('HelXlo');
'color': 'blue', 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', () { group('invert', () {
final a = Delta().insert('Hello'); test('insert', () {
final b = Delta().retain(3).insert('X'); final delta = Delta().retain(2).insert('A');
final expected = Delta().insert('HelXlo'); final base = Delta().insert('12346');
expect(a.compose(b), expected); final expected = Delta().retain(2).delete(1);
}); final inverted = delta.invert(base);
test('insert and delete ordering', () { expect(expected, inverted);
final a = Delta().insert('Hello'); expect(base.compose(delta).compose(inverted), base);
final b = Delta().insert('Hello'); });
final insertFirst = Delta().retain(3).insert('X').delete(1); test('delete', () {
final deleteFirst = Delta().retain(3).delete(1).insert('X'); final delta = Delta().retain(2).delete(3);
final expected = Delta().insert('HelXo'); final base = Delta().insert('123456');
expect(a.compose(insertFirst), expected); final expected = Delta().retain(2).insert('345');
expect(b.compose(deleteFirst), expected); final inverted = delta.invert(base);
}); expect(expected, inverted);
test('delete entire text', () { expect(base.compose(delta).compose(inverted), base);
final a = Delta().retain(4).insert('Hello'); });
final b = Delta().delete(9); // test('retain', () {
final expected = Delta().delete(4); // final delta = Delta().retain(2).retain(3, {'bold': true});
expect(a.compose(b), expected); // final base = Delta().insert('123456');
}); // final expected = Delta().retain(2).retain(3, {'bold': null});
test('retain more than length of text', () { // final inverted = delta.invert(base);
final a = Delta().insert('Hello'); // expect(expected, inverted);
final b = Delta().retain(10); // expect(base.compose(delta).compose(inverted), base);
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);
}); });
} }