From 9211afc47998a9c337ef953cb0c5f27ed8be0fa3 Mon Sep 17 00:00:00 2001 From: Xin Yao Date: Fri, 26 Feb 2021 00:33:59 -0800 Subject: [PATCH] Remove quill_delta dependency --- lib/models/documents/document.dart | 2 +- lib/models/documents/history.dart | 2 +- lib/models/documents/nodes/block.dart | 2 +- lib/models/documents/nodes/leaf.dart | 2 +- lib/models/documents/nodes/line.dart | 2 +- lib/models/documents/nodes/node.dart | 2 +- lib/models/quill_delta.dart | 688 ++++++++++++++++++++++++++ lib/models/rules/delete.dart | 2 +- lib/models/rules/format.dart | 2 +- lib/models/rules/insert.dart | 2 +- lib/models/rules/rule.dart | 2 +- lib/utils/diff_delta.dart | 2 +- lib/widgets/controller.dart | 2 +- pubspec.lock | 7 - pubspec.yaml | 1 - 15 files changed, 700 insertions(+), 20 deletions(-) create mode 100644 lib/models/quill_delta.dart diff --git a/lib/models/documents/document.dart b/lib/models/documents/document.dart index 1573f9af..73391d7b 100644 --- a/lib/models/documents/document.dart +++ b/lib/models/documents/document.dart @@ -4,7 +4,7 @@ import 'package:flutter_quill/models/documents/nodes/block.dart'; import 'package:flutter_quill/models/documents/nodes/container.dart'; import 'package:flutter_quill/models/documents/nodes/line.dart'; import 'package:flutter_quill/models/documents/style.dart'; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:tuple/tuple.dart'; import '../rules/rule.dart'; diff --git a/lib/models/documents/history.dart b/lib/models/documents/history.dart index e0d26948..ea71afe9 100644 --- a/lib/models/documents/history.dart +++ b/lib/models/documents/history.dart @@ -1,4 +1,4 @@ -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:tuple/tuple.dart'; import 'document.dart'; diff --git a/lib/models/documents/nodes/block.dart b/lib/models/documents/nodes/block.dart index 710de779..d48dc38d 100644 --- a/lib/models/documents/nodes/block.dart +++ b/lib/models/documents/nodes/block.dart @@ -1,4 +1,4 @@ -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'container.dart'; import 'line.dart'; diff --git a/lib/models/documents/nodes/leaf.dart b/lib/models/documents/nodes/leaf.dart index aefb574d..13c7b6f0 100644 --- a/lib/models/documents/nodes/leaf.dart +++ b/lib/models/documents/nodes/leaf.dart @@ -1,6 +1,6 @@ import 'dart:math' as math; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import '../style.dart'; import 'embed.dart'; diff --git a/lib/models/documents/nodes/line.dart b/lib/models/documents/nodes/line.dart index 2727e101..90b19f03 100644 --- a/lib/models/documents/nodes/line.dart +++ b/lib/models/documents/nodes/line.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/nodes/node.dart'; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import '../style.dart'; import 'block.dart'; diff --git a/lib/models/documents/nodes/node.dart b/lib/models/documents/nodes/node.dart index 6327a1bf..9c52f9c7 100644 --- a/lib/models/documents/nodes/node.dart +++ b/lib/models/documents/nodes/node.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'package:flutter_quill/models/documents/style.dart'; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import '../attribute.dart'; import 'container.dart'; diff --git a/lib/models/quill_delta.dart b/lib/models/quill_delta.dart new file mode 100644 index 00000000..40a4998b --- /dev/null +++ b/lib/models/quill_delta.dart @@ -0,0 +1,688 @@ +// Copyright (c) 2018, Anatoly Pulyaevskiy. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +/// Implementation of Quill Delta format in Dart. +library quill_delta; + +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:quiver_hashcode/hashcode.dart'; + +const _attributeEquality = DeepCollectionEquality(); +const _valueEquality = DeepCollectionEquality(); + +/// Decoder function to convert raw `data` object into a user-defined data type. +/// +/// Useful with embedded content. +typedef DataDecoder = Object Function(Object data); + +/// Default data decoder which simply passes through the original value. +Object _passThroughDataDecoder(Object data) => data; + +/// Operation performed on a rich-text document. +class Operation { + /// Key of insert operations. + static const String insertKey = 'insert'; + + /// Key of delete operations. + static const String deleteKey = 'delete'; + + /// Key of retain operations. + static const String retainKey = 'retain'; + + /// Key of attributes collection. + static const String attributesKey = 'attributes'; + + static const List _validKeys = [insertKey, deleteKey, retainKey]; + + /// Key of this operation, can be "insert", "delete" or "retain". + final String key; + + /// Length of this operation. + final int length; + + /// Payload of "insert" operation, for other types is set to empty string. + final Object data; + + /// Rich-text attributes set by this operation, can be `null`. + Map get attributes => + _attributes == null ? null : Map.from(_attributes); + final Map _attributes; + + Operation._(this.key, this.length, this.data, Map attributes) + : assert(key != null && length != null && data != null), + assert(_validKeys.contains(key), 'Invalid operation key "$key".'), + assert(() { + if (key != Operation.insertKey) return true; + return data is String ? data.length == length : length == 1; + }(), 'Length of insert operation must be equal to the data length.'), + _attributes = + attributes != null ? Map.from(attributes) : null; + + /// Creates new [Operation] from JSON payload. + /// + /// If `dataDecoder` parameter is not null then it is used to additionally + /// decode the operation's data object. Only applied to insert operations. + static Operation fromJson(Map data, {DataDecoder dataDecoder}) { + dataDecoder ??= _passThroughDataDecoder; + final map = Map.from(data); + if (map.containsKey(Operation.insertKey)) { + final data = dataDecoder(map[Operation.insertKey]); + final dataLength = data is String ? data.length : 1; + return Operation._( + Operation.insertKey, dataLength, data, map[Operation.attributesKey]); + } else if (map.containsKey(Operation.deleteKey)) { + final int length = map[Operation.deleteKey]; + return Operation._(Operation.deleteKey, length, '', null); + } else if (map.containsKey(Operation.retainKey)) { + final int length = map[Operation.retainKey]; + return Operation._( + Operation.retainKey, length, '', map[Operation.attributesKey]); + } + throw ArgumentError.value(data, 'Invalid data for Delta operation.'); + } + + /// Returns JSON-serializable representation of this operation. + Map toJson() { + final json = {key: value}; + if (_attributes != null) json[Operation.attributesKey] = attributes; + return json; + } + + /// Creates operation which deletes [length] of characters. + factory Operation.delete(int length) => + Operation._(Operation.deleteKey, length, '', null); + + /// Creates operation which inserts [text] with optional [attributes]. + factory Operation.insert(dynamic data, [Map attributes]) => + Operation._(Operation.insertKey, data is String ? data.length : 1, data, + attributes); + + /// Creates operation which retains [length] of characters and optionally + /// applies attributes. + factory Operation.retain(int length, [Map attributes]) => + Operation._(Operation.retainKey, length, '', attributes); + + /// Returns value of this operation. + /// + /// For insert operations this returns text, for delete and retain - length. + dynamic get value => (key == Operation.insertKey) ? data : length; + + /// Returns `true` if this is a delete operation. + bool get isDelete => key == Operation.deleteKey; + + /// Returns `true` if this is an insert operation. + bool get isInsert => key == Operation.insertKey; + + /// Returns `true` if this is a retain operation. + bool get isRetain => key == Operation.retainKey; + + /// Returns `true` if this operation has no attributes, e.g. is plain text. + bool get isPlain => (_attributes == null || _attributes.isEmpty); + + /// Returns `true` if this operation sets at least one attribute. + bool get isNotPlain => !isPlain; + + /// Returns `true` is this operation is empty. + /// + /// An operation is considered empty if its [length] is equal to `0`. + bool get isEmpty => length == 0; + + /// Returns `true` is this operation is not empty. + bool get isNotEmpty => length > 0; + + @override + bool operator ==(other) { + if (identical(this, other)) return true; + if (other is! Operation) return false; + Operation typedOther = other; + return key == typedOther.key && + length == typedOther.length && + _valueEquality.equals(data, typedOther.data) && + hasSameAttributes(typedOther); + } + + /// Returns `true` if this operation has attribute specified by [name]. + bool hasAttribute(String name) => isNotPlain && _attributes.containsKey(name); + + /// Returns `true` if [other] operation has the same attributes as this one. + bool hasSameAttributes(Operation other) { + return _attributeEquality.equals(_attributes, other._attributes); + } + + @override + int get hashCode { + if (_attributes != null && _attributes.isNotEmpty) { + final attrsHash = + hashObjects(_attributes.entries.map((e) => hash2(e.key, e.value))); + return hash3(key, value, attrsHash); + } + return hash2(key, value); + } + + @override + String toString() { + final attr = attributes == null ? '' : ' + $attributes'; + final text = isInsert + ? (data is String + ? (data as String).replaceAll('\n', '⏎') + : data.toString()) + : '$length'; + return '$key⟨ $text ⟩$attr'; + } +} + +/// Delta represents a document or a modification of a document as a sequence of +/// insert, delete and retain operations. +/// +/// Delta consisting of only "insert" operations is usually referred to as +/// "document delta". When delta includes also "retain" or "delete" operations +/// it is a "change delta". +class Delta { + /// Transforms two attribute sets. + static Map transformAttributes( + Map a, Map b, bool priority) { + if (a == null) return b; + if (b == null) return null; + + if (!priority) return b; + + final result = b.keys.fold>({}, (attributes, key) { + if (!a.containsKey(key)) attributes[key] = b[key]; + return attributes; + }); + + return result.isEmpty ? null : result; + } + + /// Composes two attribute sets. + static Map composeAttributes( + Map a, Map b, + {bool keepNull = false}) { + a ??= const {}; + b ??= const {}; + + final result = Map.from(a)..addAll(b); + final keys = result.keys.toList(growable: false); + + if (!keepNull) { + for (final key in keys) { + if (result[key] == null) result.remove(key); + } + } + + return result.isEmpty ? null : result; + } + + ///get anti-attr result base on base + static Map invertAttributes( + Map attr, Map base) { + attr ??= const {}; + base ??= const {}; + + var baseInverted = base.keys.fold({}, (memo, key) { + if (base[key] != attr[key] && attr.containsKey(key)) { + memo[key] = base[key]; + } + return memo; + }); + + var inverted = + Map.from(attr.keys.fold(baseInverted, (memo, key) { + if (base[key] != attr[key] && !base.containsKey(key)) { + memo[key] = null; + } + return memo; + })); + return inverted; + } + + final List _operations; + + int _modificationCount = 0; + + Delta._(List operations) + : assert(operations != null), + _operations = operations; + + /// Creates new empty [Delta]. + factory Delta() => Delta._([]); + + /// Creates new [Delta] from [other]. + factory Delta.from(Delta other) => + Delta._(List.from(other._operations)); + + /// Creates [Delta] from de-serialized JSON representation. + /// + /// If `dataDecoder` parameter is not null then it is used to additionally + /// decode the operation's data object. Only applied to insert operations. + static Delta fromJson(List data, {DataDecoder dataDecoder}) { + return Delta._(data + .map((op) => Operation.fromJson(op, dataDecoder: dataDecoder)) + .toList()); + } + + /// Returns list of operations in this delta. + List toList() => List.from(_operations); + + /// Returns JSON-serializable version of this delta. + List toJson() => toList(); + + /// Returns `true` if this delta is empty. + bool get isEmpty => _operations.isEmpty; + + /// Returns `true` if this delta is not empty. + bool get isNotEmpty => _operations.isNotEmpty; + + /// Returns number of operations in this delta. + int get length => _operations.length; + + /// Returns [Operation] at specified [index] in this delta. + Operation operator [](int index) => _operations[index]; + + /// Returns [Operation] at specified [index] in this delta. + Operation elementAt(int index) => _operations.elementAt(index); + + /// Returns the first [Operation] in this delta. + Operation get first => _operations.first; + + /// Returns the last [Operation] in this delta. + Operation get last => _operations.last; + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) return true; + if (other is! Delta) return false; + Delta typedOther = other; + final comparator = + ListEquality(const DefaultEquality()); + return comparator.equals(_operations, typedOther._operations); + } + + @override + int get hashCode => hashObjects(_operations); + + /// Retain [count] of characters from current position. + void retain(int count, [Map attributes]) { + assert(count >= 0); + if (count == 0) return; // no-op + push(Operation.retain(count, attributes)); + } + + /// Insert [data] at current position. + void insert(dynamic data, [Map attributes]) { + assert(data != null); + if (data is String && data.isEmpty) return; // no-op + push(Operation.insert(data, attributes)); + } + + /// Delete [count] characters from current position. + void delete(int count) { + assert(count >= 0); + if (count == 0) return; + push(Operation.delete(count)); + } + + void _mergeWithTail(Operation operation) { + assert(isNotEmpty); + assert(operation != null && last.key == operation.key); + assert(operation.data is String && last.data is String); + + final length = operation.length + last.length; + final lastText = last.data as String; + final opText = operation.data as String; + final resultText = lastText + opText; + final index = _operations.length; + _operations.replaceRange(index - 1, index, [ + Operation._(operation.key, length, resultText, operation.attributes), + ]); + } + + /// Pushes new operation into this delta. + /// + /// Performs compaction by composing [operation] with current tail operation + /// of this delta, when possible. For instance, if current tail is + /// `insert('abc')` and pushed operation is `insert('123')` then existing + /// tail is replaced with `insert('abc123')` - a compound result of the two + /// operations. + void push(Operation operation) { + if (operation.isEmpty) return; + + var index = _operations.length; + final lastOp = _operations.isNotEmpty ? _operations.last : null; + if (lastOp != null) { + if (lastOp.isDelete && operation.isDelete) { + _mergeWithTail(operation); + return; + } + + if (lastOp.isDelete && operation.isInsert) { + index -= 1; // Always insert before deleting + final nLastOp = (index > 0) ? _operations.elementAt(index - 1) : null; + if (nLastOp == null) { + _operations.insert(0, operation); + return; + } + } + + if (lastOp.isInsert && operation.isInsert) { + if (lastOp.hasSameAttributes(operation) && + operation.data is String && + lastOp.data is String) { + _mergeWithTail(operation); + return; + } + } + + if (lastOp.isRetain && operation.isRetain) { + if (lastOp.hasSameAttributes(operation)) { + _mergeWithTail(operation); + return; + } + } + } + if (index == _operations.length) { + _operations.add(operation); + } else { + final opAtIndex = _operations.elementAt(index); + _operations.replaceRange(index, index + 1, [operation, opAtIndex]); + } + _modificationCount++; + } + + /// Composes next operation from [thisIter] and [otherIter]. + /// + /// Returns new operation or `null` if operations from [thisIter] and + /// [otherIter] nullify each other. For instance, for the pair `insert('abc')` + /// and `delete(3)` composition result would be empty string. + Operation _composeOperation(DeltaIterator thisIter, DeltaIterator otherIter) { + if (otherIter.isNextInsert) return otherIter.next(); + if (thisIter.isNextDelete) return thisIter.next(); + + final length = math.min(thisIter.peekLength(), otherIter.peekLength()); + final thisOp = thisIter.next(length); + final otherOp = otherIter.next(length); + assert(thisOp.length == otherOp.length); + + if (otherOp.isRetain) { + final attributes = composeAttributes( + thisOp.attributes, + otherOp.attributes, + keepNull: thisOp.isRetain, + ); + if (thisOp.isRetain) { + return Operation.retain(thisOp.length, attributes); + } else if (thisOp.isInsert) { + return Operation.insert(thisOp.data, attributes); + } else { + throw StateError('Unreachable'); + } + } else { + // otherOp == delete && thisOp in [retain, insert] + assert(otherOp.isDelete); + if (thisOp.isRetain) return otherOp; + assert(thisOp.isInsert); + // otherOp(delete) + thisOp(insert) => null + } + return null; + } + + /// Composes this delta with [other] and returns new [Delta]. + /// + /// It is not required for this and [other] delta to represent a document + /// delta (consisting only of insert operations). + Delta compose(Delta other) { + final result = Delta(); + final thisIter = DeltaIterator(this); + final otherIter = DeltaIterator(other); + + while (thisIter.hasNext || otherIter.hasNext) { + final newOp = _composeOperation(thisIter, otherIter); + if (newOp != null) result.push(newOp); + } + return result..trim(); + } + + /// Transforms next operation from [otherIter] against next operation in + /// [thisIter]. + /// + /// Returns `null` if both operations nullify each other. + Operation _transformOperation( + DeltaIterator thisIter, DeltaIterator otherIter, bool priority) { + if (thisIter.isNextInsert && (priority || !otherIter.isNextInsert)) { + return Operation.retain(thisIter.next().length); + } else if (otherIter.isNextInsert) { + return otherIter.next(); + } + + final length = math.min(thisIter.peekLength(), otherIter.peekLength()); + final thisOp = thisIter.next(length); + final otherOp = otherIter.next(length); + assert(thisOp.length == otherOp.length); + + // At this point only delete and retain operations are possible. + if (thisOp.isDelete) { + // otherOp is either delete or retain, so they nullify each other. + return null; + } else if (otherOp.isDelete) { + return otherOp; + } else { + // Retain otherOp which is either retain or insert. + return Operation.retain( + length, + transformAttributes(thisOp.attributes, otherOp.attributes, priority), + ); + } + } + + /// Transforms [other] delta against operations in this delta. + Delta transform(Delta other, bool priority) { + final result = Delta(); + final thisIter = DeltaIterator(this); + final otherIter = DeltaIterator(other); + + while (thisIter.hasNext || otherIter.hasNext) { + final newOp = _transformOperation(thisIter, otherIter, priority); + if (newOp != null) result.push(newOp); + } + return result..trim(); + } + + /// Removes trailing retain operation with empty attributes, if present. + void trim() { + if (isNotEmpty) { + final last = _operations.last; + if (last.isRetain && last.isPlain) _operations.removeLast(); + } + } + + /// Concatenates [other] with this delta and returns the result. + Delta concat(Delta other) { + final result = Delta.from(this); + if (other.isNotEmpty) { + // In case first operation of other can be merged with last operation in + // our list. + result.push(other._operations.first); + result._operations.addAll(other._operations.sublist(1)); + } + return result; + } + + /// Inverts this delta against [base]. + /// + /// Returns new delta which negates effect of this delta when applied to + /// [base]. This is an equivalent of "undo" operation on deltas. + Delta invert(Delta base) { + final inverted = Delta(); + if (base.isEmpty) return inverted; + + var baseIndex = 0; + for (final op in _operations) { + if (op.isInsert) { + inverted.delete(op.length); + } else if (op.isRetain && op.isPlain) { + inverted.retain(op.length, null); + baseIndex += op.length; + } else if (op.isDelete || (op.isRetain && op.isNotPlain)) { + final length = op.length; + final sliceDelta = base.slice(baseIndex, baseIndex + length); + sliceDelta.toList().forEach((baseOp) { + if (op.isDelete) { + inverted.push(baseOp); + } else if (op.isRetain && op.isNotPlain) { + var invertAttr = invertAttributes(op.attributes, baseOp.attributes); + inverted.retain( + baseOp.length, invertAttr.isEmpty ? null : invertAttr); + } + }); + baseIndex += length; + } else { + throw StateError('Unreachable'); + } + } + inverted.trim(); + return inverted; + } + + /// Returns slice of this delta from [start] index (inclusive) to [end] + /// (exclusive). + Delta slice(int start, [int end]) { + final delta = Delta(); + var index = 0; + var opIterator = DeltaIterator(this); + + final actualEnd = end ?? double.infinity; + + while (index < actualEnd && opIterator.hasNext) { + Operation op; + if (index < start) { + op = opIterator.next(start - index); + } else { + op = opIterator.next(actualEnd - index); + delta.push(op); + } + index += op.length; + } + return delta; + } + + /// Transforms [index] against this delta. + /// + /// Any "delete" operation before specified [index] shifts it backward, as + /// well as any "insert" operation shifts it forward. + /// + /// The [force] argument is used to resolve scenarios when there is an + /// insert operation at the same position as [index]. If [force] is set to + /// `true` (default) then position is forced to shift forward, otherwise + /// position stays at the same index. In other words setting [force] to + /// `false` gives higher priority to the transformed position. + /// + /// Useful to adjust caret or selection positions. + int transformPosition(int index, {bool force = true}) { + final iter = DeltaIterator(this); + var offset = 0; + while (iter.hasNext && offset <= index) { + final op = iter.next(); + if (op.isDelete) { + index -= math.min(op.length, index - offset); + continue; + } else if (op.isInsert && (offset < index || force)) { + index += op.length; + } + offset += op.length; + } + return index; + } + + @override + String toString() => _operations.join('\n'); +} + +/// Specialized iterator for [Delta]s. +class DeltaIterator { + final Delta delta; + final int _modificationCount; + int _index = 0; + num _offset = 0; + + DeltaIterator(this.delta) : _modificationCount = delta._modificationCount; + + bool get isNextInsert => nextOperationKey == Operation.insertKey; + + bool get isNextDelete => nextOperationKey == Operation.deleteKey; + + bool get isNextRetain => nextOperationKey == Operation.retainKey; + + String get nextOperationKey { + if (_index < delta.length) { + return delta.elementAt(_index).key; + } else { + return null; + } + } + + bool get hasNext => peekLength() < double.infinity; + + /// Returns length of next operation without consuming it. + /// + /// Returns [double.infinity] if there is no more operations left to iterate. + num peekLength() { + if (_index < delta.length) { + final operation = delta._operations[_index]; + return operation.length - _offset; + } + return double.infinity; + } + + /// Consumes and returns next operation. + /// + /// Optional [length] specifies maximum length of operation to return. Note + /// that actual length of returned operation may be less than specified value. + Operation next([num length = double.infinity]) { + assert(length != null); + + if (_modificationCount != delta._modificationCount) { + throw ConcurrentModificationError(delta); + } + + if (_index < delta.length) { + final op = delta.elementAt(_index); + final opKey = op.key; + final opAttributes = op.attributes; + final _currentOffset = _offset; + final actualLength = math.min(op.length - _currentOffset, length); + if (actualLength == op.length - _currentOffset) { + _index++; + _offset = 0; + } else { + _offset += actualLength; + } + final opData = op.isInsert && op.data is String + ? (op.data as String) + .substring(_currentOffset, _currentOffset + actualLength) + : op.data; + final opIsNotEmpty = + opData is String ? opData.isNotEmpty : true; // embeds are never empty + final opLength = opData is String ? opData.length : 1; + final int opActualLength = opIsNotEmpty ? opLength : actualLength; + return Operation._(opKey, opActualLength, opData, opAttributes); + } + return Operation.retain(length); + } + + /// Skips [length] characters in source delta. + /// + /// Returns last skipped operation, or `null` if there was nothing to skip. + Operation skip(int length) { + var skipped = 0; + Operation op; + while (skipped < length && hasNext) { + final opLength = peekLength(); + final skip = math.min(length - skipped, opLength); + op = next(skip); + skipped += op.length; + } + return op; + } +} diff --git a/lib/models/rules/delete.dart b/lib/models/rules/delete.dart index cac220d1..6cbb28ff 100644 --- a/lib/models/rules/delete.dart +++ b/lib/models/rules/delete.dart @@ -1,6 +1,6 @@ import 'package:flutter_quill/models/documents/attribute.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:flutter_quill/models/rules/rule.dart'; -import 'package:quill_delta/quill_delta.dart'; abstract class DeleteRule extends Rule { const DeleteRule(); diff --git a/lib/models/rules/format.dart b/lib/models/rules/format.dart index 13f7b518..755bc0bb 100644 --- a/lib/models/rules/format.dart +++ b/lib/models/rules/format.dart @@ -1,6 +1,6 @@ import 'package:flutter_quill/models/documents/attribute.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:flutter_quill/models/rules/rule.dart'; -import 'package:quill_delta/quill_delta.dart'; abstract class FormatRule extends Rule { const FormatRule(); diff --git a/lib/models/rules/insert.dart b/lib/models/rules/insert.dart index 4715ae87..1973d981 100644 --- a/lib/models/rules/insert.dart +++ b/lib/models/rules/insert.dart @@ -1,7 +1,7 @@ import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/style.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:flutter_quill/models/rules/rule.dart'; -import 'package:quill_delta/quill_delta.dart'; import 'package:tuple/tuple.dart'; abstract class InsertRule extends Rule { diff --git a/lib/models/rules/rule.dart b/lib/models/rules/rule.dart index d0e3bc14..13c7d12e 100644 --- a/lib/models/rules/rule.dart +++ b/lib/models/rules/rule.dart @@ -1,6 +1,6 @@ import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/document.dart'; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'delete.dart'; import 'format.dart'; diff --git a/lib/utils/diff_delta.dart b/lib/utils/diff_delta.dart index 49a06dab..673dacd2 100644 --- a/lib/utils/diff_delta.dart +++ b/lib/utils/diff_delta.dart @@ -1,6 +1,6 @@ import 'dart:math' as math; -import 'package:quill_delta/quill_delta.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; const Set WHITE_SPACE = { 0x9, diff --git a/lib/widgets/controller.dart b/lib/widgets/controller.dart index 5bd6b2d8..abf2a059 100644 --- a/lib/widgets/controller.dart +++ b/lib/widgets/controller.dart @@ -5,8 +5,8 @@ import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/document.dart'; import 'package:flutter_quill/models/documents/nodes/embed.dart'; import 'package:flutter_quill/models/documents/style.dart'; +import 'package:flutter_quill/models/quill_delta.dart'; import 'package:flutter_quill/utils/diff_delta.dart'; -import 'package:quill_delta/quill_delta.dart'; import 'package:tuple/tuple.dart'; class QuillController extends ChangeNotifier { diff --git a/pubspec.lock b/pubspec.lock index 8cdc5c8f..75d48e83 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -212,13 +212,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" - quill_delta: - dependency: "direct main" - description: - name: quill_delta - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" quiver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 87e8ab72..4d1bdf7b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,6 @@ environment: dependencies: flutter: sdk: flutter - quill_delta: ^2.0.0 quiver_hashcode: ^2.0.0 collection: ^1.15.0 tuple: ^2.0.0-nullsafety.0