diff --git a/packages/parchment/lib/src/heuristics.dart b/packages/parchment/lib/src/heuristics.dart index 1c8636ca..6b4a6e8b 100644 --- a/packages/parchment/lib/src/heuristics.dart +++ b/packages/parchment/lib/src/heuristics.dart @@ -34,11 +34,13 @@ class ParchmentHeuristics { MarkdownBlockShortcutsInsertRule(), // Lines PreserveLineStyleOnSplitRule(), - ResetLineFormatOnNewLineRule(), + ResetHeadingFormatOnNewLineRule(), AutoTextDirectionRule(), // Inlines AutoFormatLinksRule(), PreserveInlineStylesRule(), + // Catch new line + PreserveLineStyleOnNewLineInsertRule(), // Catch-all CatchAllInsertRule(), ], diff --git a/packages/parchment/lib/src/heuristics/insert_rules.dart b/packages/parchment/lib/src/heuristics/insert_rules.dart index 81bc2fb8..4f5977f4 100644 --- a/packages/parchment/lib/src/heuristics/insert_rules.dart +++ b/packages/parchment/lib/src/heuristics/insert_rules.dart @@ -64,6 +64,25 @@ class CatchAllInsertRule extends InsertRule { } } +/// Fallback rule for new line which retains line styles +class PreserveLineStyleOnNewLineInsertRule extends InsertRule { + const PreserveLineStyleOnNewLineInsertRule(); + + @override + Delta? apply(Delta document, int index, Object data) { + if (!isOnlyNewLines(data)) return null; + + final iter = DeltaIterator(document); + iter.skip(index); + final nextNewline = _findNextNewline(iter); + if (nextNewline.isEmpty) return null; + + return Delta() + ..retain(index) + ..insert(data, nextNewline.op?.attributes); + } +} + /// Preserves line format when user splits the line into two. /// /// This rule ignores scenarios when the line is split on its edge, meaning @@ -113,13 +132,13 @@ class PreserveLineStyleOnSplitRule extends InsertRule { } } -/// Resets format for a newly inserted line when insert occurred at the end -/// of a line (right before a newline). +/// Resets heading format for a newly inserted line when insert occurred at +/// the end of a line (right before a newline). /// /// This handles scenarios when a new line is added when at the end of a /// heading line. The newly added line should be a regular paragraph. -class ResetLineFormatOnNewLineRule extends InsertRule { - const ResetLineFormatOnNewLineRule(); +class ResetHeadingFormatOnNewLineRule extends InsertRule { + const ResetHeadingFormatOnNewLineRule(); @override Delta? apply(Delta document, int index, Object data) { @@ -144,8 +163,7 @@ class ResetLineFormatOnNewLineRule extends InsertRule { return Delta() ..retain(index) ..insert('\n', target.attributes) - ..retain(1, resetStyle) - ..trim(); + ..retain(1, resetStyle); } } return null; diff --git a/packages/parchment/lib/src/heuristics/utils.dart b/packages/parchment/lib/src/heuristics/utils.dart index a7429a6f..c5619bf2 100644 --- a/packages/parchment/lib/src/heuristics/utils.dart +++ b/packages/parchment/lib/src/heuristics/utils.dart @@ -43,3 +43,8 @@ bool isBlockEmbed(Object data) { } return false; } + +bool isOnlyNewLines(Object data) { + if (data is! String) return false; + return data.replaceAll('\n', '').isEmpty; +} \ No newline at end of file diff --git a/packages/parchment/test/heuristics/insert_rules_test.dart b/packages/parchment/test/heuristics/insert_rules_test.dart index cabb0da5..e6f7da84 100644 --- a/packages/parchment/test/heuristics/insert_rules_test.dart +++ b/packages/parchment/test/heuristics/insert_rules_test.dart @@ -22,6 +22,27 @@ void main() { }); }); + group('$PreserveLineStyleOnNewLineInsertRule', () { + final rule = PreserveLineStyleOnNewLineInsertRule(); + + test('data is not new line only', () { + final doc = Delta()..insert('Test\n'); + final actual = rule.apply(doc, 0, '\nOne\n'); + expect(actual, isNull); + }); + + test('preserves line styles', () { + final doc = Delta() + ..insert('Test') + ..insert('\n', ParchmentAttribute.right.toJson()); + final actual = rule.apply(doc, 4, '\n'); + final expected = Delta() + ..retain(4) + ..insert('\n', ParchmentAttribute.right.toJson()); + expect(actual, expected); + }); + }); + group('$PreserveLineStyleOnSplitRule', () { final rule = PreserveLineStyleOnSplitRule(); @@ -59,8 +80,8 @@ void main() { }); }); - group('$ResetLineFormatOnNewLineRule', () { - final rule = const ResetLineFormatOnNewLineRule(); + group('$ResetHeadingFormatOnNewLineRule', () { + final rule = const ResetHeadingFormatOnNewLineRule(); test('applies when line-break is inserted at the end of line', () { final doc = Delta()