fix: use retain instead of insert/delete for same attr (#937)
* fix: use retain instead of insert/delete for same attr * test: add delta diff test
This commit is contained in:
parent
5cb871d3f5
commit
450912305a
3 changed files with 73 additions and 2 deletions
|
|
@ -52,6 +52,23 @@ Attributes invertAttributes(Attributes? from, Attributes? to) {
|
|||
return attributes;
|
||||
}
|
||||
|
||||
Attributes? diffAttributes(
|
||||
Map<String, dynamic>? from,
|
||||
Map<String, dynamic>? to,
|
||||
) {
|
||||
from ??= const {};
|
||||
to ??= const {};
|
||||
final attributes = Attributes.from({});
|
||||
|
||||
for (final key in (from.keys.toList()..addAll(to.keys))) {
|
||||
if (from[key] != to[key]) {
|
||||
attributes[key] = to.containsKey(key) ? to[key] : null;
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
int hashAttributes(Attributes base) => Object.hashAllUnordered(
|
||||
base.entries.map((e) => Object.hash(e.key, e.value)),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ const int _maxInt = 9007199254740991;
|
|||
|
||||
sealed class TextOperation {
|
||||
Attributes? get attributes;
|
||||
|
||||
// available for TextInsert, for TextDelete and TextRetain, it's null
|
||||
Object? get data => null;
|
||||
|
||||
int get length;
|
||||
|
||||
bool get isEmpty => length == 0;
|
||||
|
|
@ -81,6 +85,9 @@ class TextInsert extends TextOperation {
|
|||
@override
|
||||
int get length => text.length;
|
||||
|
||||
@override
|
||||
Object? get data => text;
|
||||
|
||||
@override
|
||||
Attributes? get attributes => _attributes != null ? {..._attributes} : null;
|
||||
|
||||
|
|
@ -402,10 +409,10 @@ class Delta extends Iterable<TextOperation> {
|
|||
);
|
||||
final thisOp = thisIter.next(opLength);
|
||||
final otherOp = otherIter.next(opLength);
|
||||
if (isAttributesEqual(thisOp.attributes, otherOp.attributes)) {
|
||||
if (thisOp.data == otherOp.data) {
|
||||
retDelta.retain(
|
||||
opLength,
|
||||
attributes: invertAttributes(
|
||||
attributes: diffAttributes(
|
||||
thisOp.attributes,
|
||||
otherOp.attributes,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -519,6 +519,53 @@ void main() {
|
|||
final diff = delta.diff(restored);
|
||||
expect(delta.compose(diff), restored);
|
||||
});
|
||||
|
||||
test('insert', () {
|
||||
final a = Delta()..insert('Hello');
|
||||
final b = Delta()..insert('Hello!');
|
||||
final expected = Delta()
|
||||
..retain(5)
|
||||
..insert('!');
|
||||
expect(a.diff(b), expected);
|
||||
});
|
||||
|
||||
test('delete', () {
|
||||
final a = Delta()..insert('Hello!');
|
||||
final b = Delta()..insert('Hello');
|
||||
final expected = Delta()
|
||||
..retain(5)
|
||||
..delete(1);
|
||||
expect(a.diff(b), expected);
|
||||
});
|
||||
|
||||
test('retain', () {
|
||||
final a = Delta()..insert('A');
|
||||
final b = Delta()..insert('A');
|
||||
final expected = Delta();
|
||||
expect(a.diff(b), expected);
|
||||
});
|
||||
|
||||
test('retain with attributes - 1', () {
|
||||
final a = Delta()..insert('Hello');
|
||||
final b = Delta()..insert('Hello', attributes: {'bold': true});
|
||||
final expected = Delta()
|
||||
..retain(
|
||||
5,
|
||||
attributes: {'bold': true},
|
||||
);
|
||||
expect(a.diff(b), expected);
|
||||
});
|
||||
|
||||
test('retain with attributes - 2', () {
|
||||
final a = Delta()..insert('Hello', attributes: {'bold': true});
|
||||
final b = Delta()..insert('Hello', attributes: {'italic': true});
|
||||
final expected = Delta()
|
||||
..retain(
|
||||
5,
|
||||
attributes: {'bold': null, 'italic': true},
|
||||
);
|
||||
expect(a.diff(b), expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue