diff --git a/lib/src/models/documents/nodes/container.dart b/lib/src/models/documents/nodes/container.dart index bc52adb6..7b5bb551 100644 --- a/lib/src/models/documents/nodes/container.dart +++ b/lib/src/models/documents/nodes/container.dart @@ -47,11 +47,14 @@ abstract base class QuillContainer 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. @@ -59,6 +62,7 @@ abstract base class QuillContainer extends Node { assert(node?.parent == null); node?.parent = this; _children.addFirst(node as Node); + clearLengthCache(); } /// Removes [node] from this container. @@ -66,6 +70,7 @@ abstract base class QuillContainer extends Node { assert(node?.parent == this); node?.parent = null; _children.remove(node as Node); + clearLengthCache(); } /// Moves children of this node to [newParent]. @@ -118,11 +123,20 @@ abstract base class QuillContainer 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) { diff --git a/lib/src/models/documents/nodes/leaf.dart b/lib/src/models/documents/nodes/leaf.dart index 676abdc4..43a5e1ff 100644 --- a/lib/src/models/documents/nodes/leaf.dart +++ b/lib/src/models/documents/nodes/leaf.dart @@ -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 @@ -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) { @@ -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!); @@ -117,7 +140,7 @@ 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; } @@ -125,7 +148,7 @@ abstract base class Leaf extends Node { // 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(); } } @@ -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; diff --git a/lib/src/models/documents/nodes/line.dart b/lib/src/models/documents/nodes/line.dart index 65e893f7..ee467d4e 100644 --- a/lib/src/models/documents/nodes/line.dart +++ b/lib/src/models/documents/nodes/line.dart @@ -127,11 +127,11 @@ base class Line extends QuillContainer { 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( @@ -145,7 +145,7 @@ base class Line extends QuillContainer { assert(style.values.every((attr) => attr.scope == AttributeScope.inline || attr.scope == AttributeScope.ignore)); - assert(index + local != thisLength); + assert(index + local != length); super.retain(index, local, style); } @@ -158,6 +158,7 @@ base class Line extends QuillContainer { @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) { diff --git a/lib/src/models/documents/nodes/node.dart b/lib/src/models/documents/nodes/node.dart index 0ef7c10a..0287dc4d 100644 --- a/lib/src/models/documents/nodes/node.dart +++ b/lib/src/models/documents/nodes/node.dart @@ -47,24 +47,41 @@ abstract base class Node extends LinkedListEntry { /// 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. @@ -100,6 +117,7 @@ abstract base class Node extends LinkedListEntry { assert(entry.parent == null && parent != null); entry.parent = parent; super.insertBefore(entry); + clearLengthCache(); } @override @@ -107,11 +125,13 @@ abstract base class Node extends LinkedListEntry { assert(entry.parent == null && parent != null); entry.parent = parent; super.insertAfter(entry); + clearLengthCache(); } @override void unlink() { assert(parent != null); + clearLengthCache(); parent = null; super.unlink(); }