import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/rules/rule.dart'; import 'package:quill_delta/quill_delta.dart'; abstract class DeleteRule extends Rule { const DeleteRule(); @override RuleType get type => RuleType.DELETE; @override validateArgs(int len, Object data, Attribute attribute) { assert(len != null); assert(data == null); assert(attribute == null); } } class CatchAllDeleteRule extends DeleteRule { const CatchAllDeleteRule(); @override Delta applyRule(Delta document, int index, {int len, Object data, Attribute attribute}) { return Delta() ..retain(index) ..delete(len); } } class PreserveLineStyleOnMergeRule extends DeleteRule { const PreserveLineStyleOnMergeRule(); @override Delta applyRule(Delta document, int index, {int len, Object data, Attribute attribute}) { DeltaIterator itr = DeltaIterator(document); itr.skip(index); Operation op = itr.next(1); if (op.data != '\n') { return null; } bool isNotPlain = op.isNotPlain; Map attrs = op.attributes; itr.skip(len - 1); Delta delta = Delta() ..retain(index) ..delete(len); while (itr.hasNext) { op = itr.next(); String text = op.data is String ? op.data as String : ''; int lineBreak = text.indexOf('\n'); if (lineBreak == -1) { delta..retain(op.length); continue; } Map attributes = op.attributes == null ? null : op.attributes.map((String key, dynamic value) => MapEntry(key, null)); if (isNotPlain) { attributes ??= {}; attributes.addAll(attrs); } delta..retain(lineBreak)..retain(1, attributes); break; } return delta; } } class EnsureEmbedLineRule extends DeleteRule { const EnsureEmbedLineRule(); @override Delta applyRule(Delta document, int index, {int len, Object data, Attribute attribute}) { DeltaIterator itr = DeltaIterator(document); Operation op = itr.skip(index); int indexDelta = 0, lengthDelta = 0, remain = len; bool embedFound = op != null && op.data is! String; bool hasLineBreakBefore = !embedFound && (op == null || (op?.data as String).endsWith('\n')); if (embedFound) { Operation candidate = itr.next(1); remain--; if (candidate.data == '\n') { indexDelta++; lengthDelta--; candidate = itr.next(1); remain--; if (candidate.data == '\n') { lengthDelta++; } } } op = itr.skip(remain); if (op != null && (op?.data is String ? op.data as String : '').endsWith('\n')) { Operation candidate = itr.next(1); if (candidate.data is! String && !hasLineBreakBefore) { embedFound = true; lengthDelta--; } } if (!embedFound) { return null; } return Delta() ..retain(index + indexDelta) ..delete(len + lengthDelta); } }