Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perf: Performance optimization #1964

Merged
merged 4 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions lib/src/models/documents/document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,7 @@ class Document {
} catch (e) {
throw StateError('_delta compose failed');
}

if (_delta != _root.toDelta()) {
throw StateError('Compose failed');
}
assert(_delta == _root.toDelta(), 'Compose failed');
final change = DocChange(originalDelta, delta, changeSource);
documentChangeObserver.add(change);
history.handleDocChange(change);
Expand Down Expand Up @@ -442,7 +439,6 @@ class Document {
doc.toString(), 'Document Delta cannot be empty.');
}

// print(doc.last.data.runtimeType);
assert((doc.last.data as String).endsWith('\n'));

var offset = 0;
Expand Down
22 changes: 18 additions & 4 deletions lib/src/models/documents/nodes/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,30 @@ abstract base class QuillContainer<T extends Node?> extends Node {
/// Always returns fresh instance.
T get defaultChild;

int? _length;

/// Adds [node] to the end of this container children list.
void add(T node) {
assert(node?.parent == null);
node?.parent = this;
_children.add(node as Node);
clearLengthCache();
}

/// Adds [node] to the beginning of this container children list.
void addFirst(T node) {
assert(node?.parent == null);
node?.parent = this;
_children.addFirst(node as Node);
clearLengthCache();
}

/// Removes [node] from this container.
void remove(T node) {
assert(node?.parent == this);
node?.parent = null;
_children.remove(node as Node);
clearLengthCache();
}

/// Moves children of this node to [newParent].
Expand Down Expand Up @@ -118,11 +123,20 @@ abstract base class QuillContainer<T extends Node?> extends Node {
.map((e) => e.toPlainText(embedBuilders, unknownEmbedBuilder))
.join();

/// Content length of this node's children.
///
/// To get number of children in this node use [childCount].
@override
int get length => _children.fold(0, (cur, node) => cur + node.length);
int get length {
_length ??= _children.fold(0, (cur, node) => (cur ?? 0) + node.length);
return _length!;
}

@override
void clearLengthCache() {
_length = null;
clearOffsetCache();
if (parent != null) {
parent!.clearLengthCache();
}
}

@override
void insert(int index, Object data, Style? style) {
Expand Down
35 changes: 29 additions & 6 deletions lib/src/models/documents/nodes/leaf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,39 @@ abstract base class Leaf extends Node {
/// Contents of this node, either a String if this is a [QuillText] or an
/// [Embed] if this is an [BlockEmbed].
Object get value => _value;

set value(Object v) {
_value = v;
_length = null;
clearOffsetCache();
}

Object _value;

@override
Line? get parent => super.parent as Line?;

int? _length;

@override
int get length {
if (_length != null) {
return _length!;
}
if (_value is String) {
return (_value as String).length;
_length = (_value as String).length;
} else {
// return 1 for embedded object
_length = 1;
}
return _length!;
}

@override
void clearLengthCache() {
if (parent != null) {
parent!.clearLengthCache();
}
// return 1 for embedded object
return 1;
}

@override
Expand All @@ -47,6 +68,7 @@ abstract base class Leaf extends Node {

@override
void insert(int index, Object data, Style? style) {
final length = this.length;
assert(index >= 0 && index <= length);
final node = Leaf(data);
if (index < length) {
Expand Down Expand Up @@ -75,6 +97,7 @@ abstract base class Leaf extends Node {

@override
void delete(int index, int? len) {
final length = this.length;
assert(index < length);

final local = math.min(length - index, len!);
Expand Down Expand Up @@ -117,15 +140,15 @@ abstract base class Leaf extends Node {
// Merging it with previous node if style is the same.
final prev = node.previous;
if (!node.isFirst && prev is QuillText && prev.style == node.style) {
prev._value = prev.value + node.value;
prev.value = prev.value + node.value;
node.unlink();
node = prev;
}

// Merging it with next node if style is the same.
final next = node.next;
if (!node.isLast && next is QuillText && next.style == node.style) {
node._value = node.value + next.value;
node.value = node.value + next.value;
next.unlink();
}
}
Expand All @@ -152,7 +175,7 @@ abstract base class Leaf extends Node {

assert(this is QuillText);
final text = _value as String;
_value = text.substring(0, index);
value = text.substring(0, index);
final split = Leaf(text.substring(index))..applyStyle(style);
insertAfter(split);
return split;
Expand Down
9 changes: 5 additions & 4 deletions lib/src/models/documents/nodes/line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ base class Line extends QuillContainer<Leaf?> {
if (style == null) {
return;
}
final thisLength = length;
final length = this.length;

final local = math.min(thisLength - index, len!);
final local = math.min(length - index, len!);
// If index is at newline character then this is a line/block style update.
final isLineFormat = (index + local == thisLength) && local == 1;
final isLineFormat = (index + local == length) && local == 1;

if (isLineFormat) {
assert(
Expand All @@ -145,7 +145,7 @@ base class Line extends QuillContainer<Leaf?> {
assert(style.values.every((attr) =>
attr.scope == AttributeScope.inline ||
attr.scope == AttributeScope.ignore));
assert(index + local != thisLength);
assert(index + local != length);
EchoEllet marked this conversation as resolved.
Show resolved Hide resolved
super.retain(index, local, style);
}

Expand All @@ -158,6 +158,7 @@ base class Line extends QuillContainer<Leaf?> {

@override
void delete(int index, int? len) {
final length = this.length;
final local = math.min(length - index, len!);
final isLFDeleted = index + local == length; // Line feed
if (isLFDeleted) {
Expand Down
36 changes: 28 additions & 8 deletions lib/src/models/documents/nodes/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,41 @@ abstract base class Node extends LinkedListEntry<Node> {
/// Length of this node in characters.
int get length;

void clearLengthCache();

Node clone() => newInstance()..applyStyle(style);

int? _offset;

/// Offset in characters of this node relative to [parent] node.
///
/// To get offset of this node in the document see [documentOffset].
int get offset {
var offset = 0;
if (_offset != null) {
return _offset!;
}

if (list == null || isFirst) {
return offset;
return 0;
}
var offset = 0;
for (final node in list!) {
if (node == this) {
break;
}
offset += node.length;
}

var cur = this;
do {
cur = cur.previous!;
offset += cur.length;
} while (!cur.isFirst);
return offset;
_offset = offset;
return _offset!;
}

void clearOffsetCache() {
_offset = null;
final next = this.next;
if (next != null) {
next.clearOffsetCache();
}
}

/// Offset in characters of this node in the document.
Expand Down Expand Up @@ -100,18 +117,21 @@ abstract base class Node extends LinkedListEntry<Node> {
assert(entry.parent == null && parent != null);
entry.parent = parent;
super.insertBefore(entry);
clearLengthCache();
}

@override
void insertAfter(Node entry) {
assert(entry.parent == null && parent != null);
entry.parent = parent;
super.insertAfter(entry);
clearLengthCache();
}

@override
void unlink() {
assert(parent != null);
clearLengthCache();
parent = null;
super.unlink();
}
Expand Down
17 changes: 9 additions & 8 deletions lib/src/models/rules/delete.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:meta/meta.dart' show immutable;

import '../../../quill_delta.dart';
import '../../models/documents/document.dart';
import '../documents/attribute.dart';
import '../documents/nodes/embeddable.dart';
import 'rule.dart';
Expand All @@ -26,9 +27,9 @@ class EnsureLastLineBreakDeleteRule extends DeleteRule {
const EnsureLastLineBreakDeleteRule();

@override
Delta? applyRule(Delta document, int index,
Delta? applyRule(Document document, int index,
{int? len, Object? data, Attribute? attribute}) {
final itr = DeltaIterator(document)..skip(index + len!);
final itr = DeltaIterator(document.toDelta())..skip(index + len!);

return Delta()
..retain(index)
Expand All @@ -43,9 +44,9 @@ class CatchAllDeleteRule extends DeleteRule {
const CatchAllDeleteRule();

@override
Delta applyRule(Delta document, int index,
Delta applyRule(Document document, int index,
{int? len, Object? data, Attribute? attribute}) {
final itr = DeltaIterator(document)..skip(index + len!);
final itr = DeltaIterator(document.toDelta())..skip(index + len!);

return Delta()
..retain(index)
Expand All @@ -64,9 +65,9 @@ class PreserveLineStyleOnMergeRule extends DeleteRule {
const PreserveLineStyleOnMergeRule();

@override
Delta? applyRule(Delta document, int index,
Delta? applyRule(Document document, int index,
{int? len, Object? data, Attribute? attribute}) {
final itr = DeltaIterator(document)..skip(index);
final itr = DeltaIterator(document.toDelta())..skip(index);
var op = itr.next(1);
if (op.data != '\n') {
return null;
Expand Down Expand Up @@ -121,9 +122,9 @@ class EnsureEmbedLineRule extends DeleteRule {
const EnsureEmbedLineRule();

@override
Delta? applyRule(Delta document, int index,
Delta? applyRule(Document document, int index,
{int? len, Object? data, Attribute? attribute}) {
final itr = DeltaIterator(document);
final itr = DeltaIterator(document.toDelta());

var op = itr.skip(index);
final opAfter = itr.skip(index + 1);
Expand Down
15 changes: 8 additions & 7 deletions lib/src/models/rules/format.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:meta/meta.dart' show immutable;

import '../../../quill_delta.dart';
import '../../models/documents/document.dart';
import '../documents/attribute.dart';
import 'rule.dart';

Expand Down Expand Up @@ -28,7 +29,7 @@ class ResolveLineFormatRule extends FormatRule {

@override
Delta? applyRule(
Delta document,
Document document,
int index, {
int? len,
Object? data,
Expand All @@ -41,7 +42,7 @@ class ResolveLineFormatRule extends FormatRule {
// Apply line styles to all newline characters within range of this
// retain operation.
var result = Delta()..retain(index);
final itr = DeltaIterator(document)..skip(index);
final itr = DeltaIterator(document.toDelta())..skip(index);
Operation op;
for (var cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
op = itr.next(len - cur);
Expand Down Expand Up @@ -119,7 +120,7 @@ class FormatLinkAtCaretPositionRule extends FormatRule {

@override
Delta? applyRule(
Delta document,
Document document,
int index, {
int? len,
Object? data,
Expand All @@ -130,7 +131,7 @@ class FormatLinkAtCaretPositionRule extends FormatRule {
}

final delta = Delta();
final itr = DeltaIterator(document);
final itr = DeltaIterator(document.toDelta());
final before = itr.skip(index), after = itr.next();
int? beg = index, retain = 0;
if (before != null && before.hasAttribute(attribute.key)) {
Expand Down Expand Up @@ -159,7 +160,7 @@ class ResolveInlineFormatRule extends FormatRule {

@override
Delta? applyRule(
Delta document,
Document document,
int index, {
int? len,
Object? data,
Expand All @@ -170,7 +171,7 @@ class ResolveInlineFormatRule extends FormatRule {
}

final delta = Delta()..retain(index);
final itr = DeltaIterator(document)..skip(index);
final itr = DeltaIterator(document.toDelta())..skip(index);

Operation op;
for (var cur = 0; cur < len! && itr.hasNext; cur += op.length!) {
Expand Down Expand Up @@ -205,7 +206,7 @@ class ResolveImageFormatRule extends FormatRule {

@override
Delta? applyRule(
Delta document,
Document document,
int index, {
int? len,
Object? data,
Expand Down
Loading