Skip to content

Commit

Permalink
Preserve line styles on new line insertion
Browse files Browse the repository at this point in the history
Fixes #158
  • Loading branch information
Amir-P committed Sep 9, 2023
1 parent 187ac7a commit 5aca4ba
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 9 deletions.
4 changes: 3 additions & 1 deletion packages/parchment/lib/src/heuristics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ class ParchmentHeuristics {
MarkdownBlockShortcutsInsertRule(),
// Lines
PreserveLineStyleOnSplitRule(),
ResetLineFormatOnNewLineRule(),
ResetHeadingFormatOnNewLineRule(),
AutoTextDirectionRule(),
// Inlines
AutoFormatLinksRule(),
PreserveInlineStylesRule(),
// Catch new line
PreserveLineStyleOnNewLineInsertRule(),
// Catch-all
CatchAllInsertRule(),
],
Expand Down
30 changes: 24 additions & 6 deletions packages/parchment/lib/src/heuristics/insert_rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions packages/parchment/lib/src/heuristics/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
25 changes: 23 additions & 2 deletions packages/parchment/test/heuristics/insert_rules_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 5aca4ba

Please sign in to comment.