From 7d3f48958df56346214d3a95cb56d6de237af583 Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Thu, 10 Sep 2026 15:12:43 +0200 Subject: [PATCH 1/2] Fix selector positioning around bottom toolbars --- .../lib/src/widgets/editor_toolbar.dart | 60 ++++++++++--- .../test/widgets/editor_toolbar_test.dart | 88 ++++++++++++++++++- 2 files changed, 134 insertions(+), 14 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 7f4f16d4..7ec73bb1 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -1274,14 +1274,32 @@ class SelectorScopeState extends State { final RenderBox presenter = context.findRenderObject() as RenderBox; final RenderBox overlayBox = overlay.context.findRenderObject() as RenderBox; - final offset = Offset(0.0, presenter.size.height); + final presenterRect = Rect.fromPoints( + presenter.localToGlobal(Offset.zero, ancestor: overlayBox), + presenter.localToGlobal( + presenter.size.bottomRight(Offset.zero), + ancestor: overlayBox, + ), + ); + // Toolbar padding belongs to the area selectors must avoid as well. + // Keep horizontal alignment with the button, but clear the whole toolbar. + final toolbar = context.findAncestorStateOfType<_FleatherToolbarState>(); + final toolbarBox = toolbar?.context.findRenderObject() as RenderBox?; + final toolbarRect = toolbarBox == null + ? presenterRect + : Rect.fromPoints( + toolbarBox.localToGlobal(Offset.zero, ancestor: overlayBox), + toolbarBox.localToGlobal( + toolbarBox.size.bottomRight(Offset.zero), + ancestor: overlayBox, + ), + ); final position = RelativeRect.fromSize( - Rect.fromPoints( - presenter.localToGlobal(offset, ancestor: overlayBox), - presenter.localToGlobal( - presenter.size.bottomRight(Offset.zero) + offset, - ancestor: overlayBox, - ), + Rect.fromLTRB( + presenterRect.left, + toolbarRect.top, + presenterRect.right, + toolbarRect.bottom, ), overlayBox.size, ); @@ -1376,8 +1394,6 @@ class _SelectorLayout extends SingleChildLayoutDelegate { // childSize: The size of the menu, when fully open, as determined by // getConstraintsForChild. - final double y = position.top; - // Find the ideal horizontal position. double x; if (position.right > childSize.width) { @@ -1395,13 +1411,31 @@ class _SelectorLayout extends SingleChildLayoutDelegate { } } - final Offset wantedPosition = Offset(x, y); - final Offset originCenter = position.toRect(Offset.zero & size).center; + final Rect presenter = position.toRect(Offset.zero & size); + final Offset originCenter = presenter.center; final Iterable subScreens = DisplayFeatureSubScreen.subScreensInBounds( Offset.zero & size, avoidBounds); final Rect subScreen = _closestScreen(subScreens, originCenter); - return _fitInsideScreen(subScreen, childSize, wantedPosition); + final double spaceBelow = + subScreen.bottom - + padding.bottom - + _selectorScreenPadding - + presenter.bottom - + _selectorScreenPadding; + final double spaceAbove = + presenter.top - + _selectorScreenPadding - + subScreen.top - + padding.top - + _selectorScreenPadding; + // Prefer below the presenter, but flip above it when that offers more room. + final bool showAbove = + childSize.height > spaceBelow && spaceAbove > spaceBelow; + final double y = showAbove + ? presenter.top - _selectorScreenPadding - childSize.height + : presenter.bottom + _selectorScreenPadding; + return _fitInsideScreen(subScreen, childSize, Offset(x, y)); } Rect _closestScreen(Iterable screens, Offset point) { @@ -1430,7 +1464,7 @@ class _SelectorLayout extends SingleChildLayoutDelegate { padding.right; } if (y < screen.top + _selectorScreenPadding + padding.top) { - y = _selectorScreenPadding + padding.top; + y = screen.top + _selectorScreenPadding + padding.top; } else if (y + childSize.height > screen.bottom - _selectorScreenPadding - padding.bottom) { y = screen.bottom - diff --git a/packages/fleather/test/widgets/editor_toolbar_test.dart b/packages/fleather/test/widgets/editor_toolbar_test.dart index c5e98810..f6cd6dfb 100644 --- a/packages/fleather/test/widgets/editor_toolbar_test.dart +++ b/packages/fleather/test/widgets/editor_toolbar_test.dart @@ -452,6 +452,92 @@ void main() { }); group('SelectorScope', () { + for (final isAtBottom in [false, true]) { + for (final keyboardHeight in [0.0, 200.0]) { + for (final isBackground in [false, true]) { + testWidgets( + '${isBackground ? 'Background' : 'Text'} color selector opens ' + '${isAtBottom ? 'above' : 'below'} the toolbar ' + 'with keyboard height $keyboardHeight', + (tester) async { + const padding = EdgeInsets.all(24); + final controller = FleatherController(); + addTearDown(controller.dispose); + await tester.pumpWidget( + MaterialApp( + builder: (context, child) => MediaQuery( + data: MediaQuery.of(context).copyWith( + padding: padding, + viewInsets: EdgeInsets.only(bottom: keyboardHeight), + ), + child: child!, + ), + home: Scaffold( + body: SafeArea( + child: Column( + children: [ + if (isAtBottom) const Spacer(), + FleatherToolbar( + children: [ + ColorButton( + controller: controller, + attributeKey: isBackground + ? ParchmentAttribute.backgroundColor + : ParchmentAttribute.foregroundColor, + nullColorLabel: isBackground + ? 'No color' + : 'Automatic', + builder: (context, value) => + const Icon(Icons.palette), + ), + ], + ), + if (!isAtBottom) const Spacer(), + ], + ), + ), + ), + ), + ); + await tester.tap(find.byType(ColorButton)); + await tester.pumpAndSettle(); + + final selector = find.byKey(const Key('color_selector')); + expect(selector, findsOneWidget); + final selectorRect = tester.getRect(selector); + final toolbarRect = tester.getRect(find.byType(FleatherToolbar)); + if (isAtBottom) { + expect(selectorRect.bottom, lessThanOrEqualTo(toolbarRect.top)); + } else { + expect( + selectorRect.top, + greaterThanOrEqualTo(toolbarRect.bottom), + ); + } + expect(selectorRect.overlaps(toolbarRect), isFalse); + + final screen = tester.getRect(find.byType(Scaffold)); + expect(selectorRect.left, greaterThanOrEqualTo(padding.left)); + expect( + selectorRect.right, + lessThanOrEqualTo(screen.right - padding.right), + ); + expect(selectorRect.top, greaterThanOrEqualTo(padding.top)); + expect( + selectorRect.bottom, + lessThanOrEqualTo( + screen.bottom - padding.bottom - keyboardHeight, + ), + ); + expect(tester.takeException(), isNull); + await tester.pumpWidget(const SizedBox()); + await tester.pumpAndSettle(throttleDuration); + }, + ); + } + } + } + testWidgets('Correctly places the selector in a visible area of screen', (WidgetTester tester) async { const padding = EdgeInsets.all(32); @@ -502,7 +588,7 @@ void main() { ); expect( tester.getRect(find.byKey(const Key('heading_selector'))).bottom, - tester.getRect(find.byType(Scaffold)).bottom - padding.bottom - 8, + lessThan(tester.getRect(find.byType(SelectHeadingButton)).top), ); }); }); From 836e461eb2c253fc3ea4e773da1e62e46b9c07b1 Mon Sep 17 00:00:00 2001 From: Alan Mantoux Date: Thu, 10 Sep 2026 15:18:32 +0200 Subject: [PATCH 2/2] fmt --- .../lib/src/widgets/editor_toolbar.dart | 6 +- .../test/widgets/editor_toolbar_test.dart | 5 +- packages/parchment/example/main.dart | 6 +- packages/parchment/lib/src/codecs/html.dart | 212 +++-- .../parchment/lib/src/codecs/html_utils.dart | 11 +- .../parchment/lib/src/codecs/markdown.dart | 171 ++-- packages/parchment/lib/src/document.dart | 58 +- .../lib/src/document/attributes.dart | 20 +- .../parchment/lib/src/document/embeds.dart | 27 +- packages/parchment/lib/src/document/leaf.dart | 19 +- packages/parchment/lib/src/document/line.dart | 40 +- packages/parchment/lib/src/document/node.dart | 6 +- packages/parchment/lib/src/heuristics.dart | 8 +- .../lib/src/heuristics/delete_rules.dart | 3 +- .../lib/src/heuristics/format_rules.dart | 32 +- .../lib/src/heuristics/insert_rules.dart | 6 +- packages/parchment/test/codecs/html_test.dart | 866 +++++++++--------- .../parchment/test/codecs/markdown_test.dart | 156 ++-- .../test/document/attributes_test.dart | 20 +- .../parchment/test/document/block_test.dart | 26 +- .../parchment/test/document/leaf_test.dart | 27 +- .../parchment/test/document/line_test.dart | 30 +- packages/parchment/test/document_test.dart | 56 +- .../test/heuristics/format_rules_test.dart | 10 +- .../test/heuristics/insert_rules_test.dart | 42 +- 25 files changed, 1101 insertions(+), 762 deletions(-) diff --git a/packages/fleather/lib/src/widgets/editor_toolbar.dart b/packages/fleather/lib/src/widgets/editor_toolbar.dart index 7ec73bb1..59f79500 100644 --- a/packages/fleather/lib/src/widgets/editor_toolbar.dart +++ b/packages/fleather/lib/src/widgets/editor_toolbar.dart @@ -1417,14 +1417,12 @@ class _SelectorLayout extends SingleChildLayoutDelegate { DisplayFeatureSubScreen.subScreensInBounds( Offset.zero & size, avoidBounds); final Rect subScreen = _closestScreen(subScreens, originCenter); - final double spaceBelow = - subScreen.bottom - + final double spaceBelow = subScreen.bottom - padding.bottom - _selectorScreenPadding - presenter.bottom - _selectorScreenPadding; - final double spaceAbove = - presenter.top - + final double spaceAbove = presenter.top - _selectorScreenPadding - subScreen.top - padding.top - diff --git a/packages/fleather/test/widgets/editor_toolbar_test.dart b/packages/fleather/test/widgets/editor_toolbar_test.dart index f6cd6dfb..07f44bd9 100644 --- a/packages/fleather/test/widgets/editor_toolbar_test.dart +++ b/packages/fleather/test/widgets/editor_toolbar_test.dart @@ -484,9 +484,8 @@ void main() { attributeKey: isBackground ? ParchmentAttribute.backgroundColor : ParchmentAttribute.foregroundColor, - nullColorLabel: isBackground - ? 'No color' - : 'Automatic', + nullColorLabel: + isBackground ? 'No color' : 'Automatic', builder: (context, value) => const Icon(Icons.palette), ), diff --git a/packages/parchment/example/main.dart b/packages/parchment/example/main.dart index c03df9fa..a2bed24c 100644 --- a/packages/parchment/example/main.dart +++ b/packages/parchment/example/main.dart @@ -3,8 +3,10 @@ import 'package:parchment/parchment.dart'; void main() { final doc = ParchmentDocument(); // Modify this document with insert, delete and format operations - doc.insert(0, - 'Parchment package provides rich text document model for Fleather editor'); + doc.insert( + 0, + 'Parchment package provides rich text document model for Fleather editor', + ); doc.format(0, 5, ParchmentAttribute.bold); // Makes first word bold. doc.format(0, 0, ParchmentAttribute.h1); // Makes first line a heading. doc.delete(23, 10); // Deletes "rich text " segment. diff --git a/packages/parchment/lib/src/codecs/html.dart b/packages/parchment/lib/src/codecs/html.dart index 8d65e81d..c90659d1 100644 --- a/packages/parchment/lib/src/codecs/html.dart +++ b/packages/parchment/lib/src/codecs/html.dart @@ -157,22 +157,28 @@ class _ParchmentHtmlEncoder extends Converter { // current and candidate are both blocks static bool isNestedList(ParchmentStyle parent, ParchmentStyle child) { final currentListAttribute = parent.values.firstWhereOrNull( - (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul); + (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul, + ); final candidateListAttribute = child.values.firstWhereOrNull( - (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul); + (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul, + ); if (currentListAttribute == null || candidateListAttribute == null) { return false; } int currentLevel = parent.values - .firstWhere((e) => e.key == ParchmentAttribute.indent.key, - orElse: () => ParchmentAttribute.indent.withLevel(0)) + .firstWhere( + (e) => e.key == ParchmentAttribute.indent.key, + orElse: () => ParchmentAttribute.indent.withLevel(0), + ) .value ?? 0; int candidateLevel = child.values - .firstWhere((e) => e.key == ParchmentAttribute.indent.key, - orElse: () => ParchmentAttribute.indent.withLevel(0)) + .firstWhere( + (e) => e.key == ParchmentAttribute.indent.key, + orElse: () => ParchmentAttribute.indent.withLevel(0), + ) .value ?? 0; return currentLevel < candidateLevel; @@ -180,11 +186,15 @@ class _ParchmentHtmlEncoder extends Converter { // Check if both attributes are lists of different type with same indentation bool isDifferentListTypeWithSameIndentationLevel( - ParchmentStyle parent, ParchmentStyle child) { + ParchmentStyle parent, + ParchmentStyle child, + ) { final currentListAttribute = parent.values.firstWhereOrNull( - (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul); + (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul, + ); final candidateListAttribute = child.values.firstWhereOrNull( - (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul); + (e) => e == ParchmentAttribute.ol || e == ParchmentAttribute.ul, + ); if (currentListAttribute == null || candidateListAttribute == null) { return false; @@ -195,13 +205,17 @@ class _ParchmentHtmlEncoder extends Converter { } int currentLevel = parent.values - .firstWhere((e) => e.key == ParchmentAttribute.indent.key, - orElse: () => ParchmentAttribute.indent.withLevel(0)) + .firstWhere( + (e) => e.key == ParchmentAttribute.indent.key, + orElse: () => ParchmentAttribute.indent.withLevel(0), + ) .value ?? 0; int candidateLevel = child.values - .firstWhere((e) => e.key == ParchmentAttribute.indent.key, - orElse: () => ParchmentAttribute.indent.withLevel(0)) + .firstWhere( + (e) => e.key == ParchmentAttribute.indent.key, + orElse: () => ParchmentAttribute.indent.withLevel(0), + ) .value ?? 0; @@ -231,9 +245,15 @@ class _ParchmentHtmlEncoder extends Converter { final subOp = Operation.insert('\n', op.attributes); final currentLineStart = state.nextLineStartPosition; state.nextLineStartPosition = _handleNewLineLineStyle( - subOp, buffer, state.nextLineStartPosition); - int padding = - _handleNewLineBlockStyle(subOp, state, currentLineStart); + subOp, + buffer, + state.nextLineStartPosition, + ); + int padding = _handleNewLineBlockStyle( + subOp, + state, + currentLineStart, + ); state.nextLineStartPosition += padding; } } @@ -241,8 +261,11 @@ class _ParchmentHtmlEncoder extends Converter { if (_isNewLine(op)) { state.isSingleLine = false; final currentLineStart = state.nextLineStartPosition; - state.nextLineStartPosition = - _handleNewLineLineStyle(op, buffer, state.nextLineStartPosition); + state.nextLineStartPosition = _handleNewLineLineStyle( + op, + buffer, + state.nextLineStartPosition, + ); int padding = _handleNewLineBlockStyle(op, state, currentLineStart); state.nextLineStartPosition += padding; } @@ -270,8 +293,10 @@ class _ParchmentHtmlEncoder extends Converter { } /// Closes all open blocks and returns the ending position. - int _closeOpenBlocks(_EncoderState state, - {bool beforePlainParagraphHandling = false}) { + int _closeOpenBlocks( + _EncoderState state, { + bool beforePlainParagraphHandling = false, + }) { final openBlockTags = state.openBlockTags; final buffer = state.buffer; final numToClose = openBlockTags.length; @@ -290,7 +315,8 @@ class _ParchmentHtmlEncoder extends Converter { // Handles the case where a nested list is followed by a plain paragraph bool isBlockTagNested = openBlockTags.length >= 2 && openBlockTags[0].style.lineAttributes.firstWhereOrNull( - (e) => e.key == ParchmentAttribute.indent.key) != + (e) => e.key == ParchmentAttribute.indent.key, + ) != null; if (i == numToClose - 1 && (!beforePlainParagraphHandling || isBlockTagNested)) { @@ -310,10 +336,14 @@ class _ParchmentHtmlEncoder extends Converter { } void _processInlineTags( - Operation op, StringBuffer buffer, List<_HtmlInlineTag> openInlineTags) { + Operation op, + StringBuffer buffer, + List<_HtmlInlineTag> openInlineTags, + ) { final parchmentStyle = ParchmentStyle.fromJson(op.attributes); - final Set inlineAttributes = - Set.from(parchmentStyle.inlineAttributes); + final Set inlineAttributes = Set.from( + parchmentStyle.inlineAttributes, + ); // Close any tag absent from inline attributes // Closing tags effectively adds the opening tag at the appropriate position @@ -388,7 +418,9 @@ class _ParchmentHtmlEncoder extends Converter { if (i == lines.length - 1) { // Done with set of paragraphs, add last paragraph to block stack. openBlockTags.insert( - 0, _HtmlBlockTag(ParchmentStyle(), initialPosition, buffer.length)); + 0, + _HtmlBlockTag(ParchmentStyle(), initialPosition, buffer.length), + ); if (lines[i].isNotEmpty) { // Elements that do not belong to a paragraph but to block of next op _writeData(subOp, buffer); @@ -403,8 +435,10 @@ class _ParchmentHtmlEncoder extends Converter { position = buffer.length; } - assert(openBlockTags.length <= 1, - 'At most one paragraph should be pushed in stack'); + assert( + openBlockTags.length <= 1, + 'At most one paragraph should be pushed in stack', + ); state.nextLineStartPosition = position; } @@ -412,7 +446,10 @@ class _ParchmentHtmlEncoder extends Converter { // returns the start position in the buffer of the next line that will be // processed int _handleNewLineLineStyle( - Operation op, StringBuffer buffer, int currentLineStart) { + Operation op, + StringBuffer buffer, + int currentLineStart, + ) { final opStyle = ParchmentStyle.fromJson(op.attributes); final newLineTag = _HtmlLineTag(opStyle, currentLineStart); if (newLineTag.style.isNotEmpty) { @@ -424,7 +461,10 @@ class _ParchmentHtmlEncoder extends Converter { // used to write html tags of blocks themselves. // returns padding induced by ex-post addition of block tags int _handleNewLineBlockStyle( - Operation op, _EncoderState state, int currentLineStart) { + Operation op, + _EncoderState state, + int currentLineStart, + ) { final buffer = state.buffer; final openBlockTags = state.openBlockTags; final opStyle = ParchmentStyle.fromJson(op.attributes); @@ -452,26 +492,32 @@ class _ParchmentHtmlEncoder extends Converter { if (isNestedList(opStyle, openBlockTags[0].style)) { final currentBlockTag = openBlockTags[0]; _writeBlockTag( - buffer, currentBlockTag..closingPosition = currentLineStart); + buffer, + currentBlockTag..closingPosition = currentLineStart, + ); openBlockTags.removeAt(0); // This handle the case where a new list (different list style) is directly // succeeding another list that ends with a nest list item if (isDifferentListTypeWithSameIndentationLevel( - opStyle, openBlockTags[0].style)) { + opStyle, + openBlockTags[0].style, + )) { final nextBlockTag = openBlockTags[0]; _writeBlockTag( - buffer, - nextBlockTag - ..closingPosition = - currentLineStart + currentBlockTag.inducedPadding); + buffer, + nextBlockTag + ..closingPosition = + currentLineStart + currentBlockTag.inducedPadding, + ); openBlockTags.removeAt(0); // If no previous style, let caller write surrounding tags if (openBlockTags.isEmpty) { var newBlockTag = _HtmlBlockTag( - opStyle, - currentLineStart + - nextBlockTag.inducedPadding + - currentBlockTag.inducedPadding); + opStyle, + currentLineStart + + nextBlockTag.inducedPadding + + currentBlockTag.inducedPadding, + ); // If no previous style, let caller write surrounding tags openBlockTags.insert(0, newBlockTag..closingPosition = buffer.length); } @@ -508,12 +554,7 @@ class _ParchmentHtmlEncoder extends Converter { } buffer.clear(); - buffer.writeAll([ - preHtml, - openTag, - innerHtml, - closeTag, - ]); + buffer.writeAll([preHtml, openTag, innerHtml, closeTag]); } void _writeBlockTag(StringBuffer buffer, _HtmlBlockTag tag) { @@ -657,8 +698,9 @@ class _HtmlLineTag extends _HtmlTag { } _HtmlLineTag(ParchmentStyle style, super.openingPosition) - : style = ParchmentStyle() - .putAll(style.lineAttributes.where((e) => isLineAttribute(e))); + : style = ParchmentStyle().putAll( + style.lineAttributes.where((e) => isLineAttribute(e)), + ); final ParchmentStyle style; @@ -681,8 +723,9 @@ class _HtmlLineTag extends _HtmlTag { } String? get alignmentCss { - var alignment = style.values - .firstWhereOrNull((e) => e.key == ParchmentAttribute.alignment.key); + var alignment = style.values.firstWhereOrNull( + (e) => e.key == ParchmentAttribute.alignment.key, + ); if (alignment == null) return null; @@ -713,8 +756,9 @@ class _HtmlLineTag extends _HtmlTag { style.values.contains(ParchmentAttribute.ol)) { return null; } - var indentation = style.values - .firstWhereOrNull((e) => e.key == ParchmentAttribute.indent.key); + var indentation = style.values.firstWhereOrNull( + (e) => e.key == ParchmentAttribute.indent.key, + ); if (indentation == null) return null; int value = indentation.value; @@ -725,8 +769,9 @@ class _HtmlLineTag extends _HtmlTag { String? get directionAttribute { if (_directionAttribute == null) { - var direction = style.values - .firstWhereOrNull((e) => e.key == ParchmentAttribute.direction.key); + var direction = style.values.firstWhereOrNull( + (e) => e.key == ParchmentAttribute.direction.key, + ); if (direction == null) { return _directionAttribute; @@ -768,8 +813,9 @@ class _HtmlLineTag extends _HtmlTag { openTag += ''; } if (attr.value == ParchmentAttribute.cl.value) { - final checked = style.values - .firstWhereOrNull((e) => e.key == ParchmentAttribute.checked.key); + final checked = style.values.firstWhereOrNull( + (e) => e.key == ParchmentAttribute.checked.key, + ); final checkedAttribute = checked != null && checked.value ? ' checked' : ''; // Checkboxes disabled so user cannot toggle them. @@ -840,10 +886,13 @@ class _HtmlBlockTag extends _HtmlTag { attribute.key == ParchmentAttribute.indent.key; } - _HtmlBlockTag(ParchmentStyle style, super.openingPosition, - [int? closingPosition]) - : style = ParchmentStyle() - .putAll(style.lineAttributes.where((e) => isBlockAttribute(e))), + _HtmlBlockTag( + ParchmentStyle style, + super.openingPosition, [ + int? closingPosition, + ]) : style = ParchmentStyle().putAll( + style.lineAttributes.where((e) => isBlockAttribute(e)), + ), closingPosition = closingPosition ?? openingPosition; final ParchmentStyle style; @@ -853,7 +902,10 @@ class _HtmlBlockTag extends _HtmlTag { _HtmlBlockTag withPadding(int padding) { return _HtmlBlockTag( - style, openingPosition + padding, closingPosition + padding); + style, + openingPosition + padding, + closingPosition + padding, + ); } @override @@ -952,8 +1004,11 @@ class _ParchmentHtmlDecoder extends Converter { node.localName == 'h6'); } - Delta _parseNode(html.Node node, - [ParchmentStyle? inlineStyle, ParchmentStyle? blockStyle]) { + Delta _parseNode( + html.Node node, [ + ParchmentStyle? inlineStyle, + ParchmentStyle? blockStyle, + ]) { inlineStyle ??= ParchmentStyle(); blockStyle ??= ParchmentStyle(); Delta delta = Delta(); @@ -1005,7 +1060,9 @@ class _ParchmentHtmlDecoder extends Converter { } ParchmentStyle _updateInlineStyle( - html.Element element, ParchmentStyle inlineStyle) { + html.Element element, + ParchmentStyle inlineStyle, + ) { ParchmentStyle updated = inlineStyle; if (element.localName == 'strong') { updated = updated.put(ParchmentAttribute.bold); @@ -1016,8 +1073,9 @@ class _ParchmentHtmlDecoder extends Converter { } else if (element.localName == 'em') { updated = updated.put(ParchmentAttribute.italic); } else if (element.localName == 'a') { - final link = - ParchmentAttribute.link.withValue(element.attributes['href']); + final link = ParchmentAttribute.link.withValue( + element.attributes['href'], + ); updated = inlineStyle.put(link); } else if (element.localName == 'span') { final css = element.attributes['style']; @@ -1026,14 +1084,16 @@ class _ParchmentHtmlDecoder extends Converter { if (style.startsWith('background-color')) { final sValue = style.split(':')[1].trim(); final color = colorValueFromCSS(sValue); - updated = - updated.put(ParchmentAttribute.backgroundColor.withColor(color)); + updated = updated.put( + ParchmentAttribute.backgroundColor.withColor(color), + ); } if (style.startsWith('color')) { final sValue = style.split(':')[1].trim(); final color = colorValueFromCSS(sValue); - updated = - updated.put(ParchmentAttribute.foregroundColor.withColor(color)); + updated = updated.put( + ParchmentAttribute.foregroundColor.withColor(color), + ); } } } @@ -1041,7 +1101,9 @@ class _ParchmentHtmlDecoder extends Converter { } ParchmentStyle _updateBlockStyle( - html.Element element, ParchmentStyle blockStyle) { + html.Element element, + ParchmentStyle blockStyle, + ) { ParchmentStyle updated = blockStyle; if (element.localName == 'h1') { updated = updated.put(ParchmentAttribute.h1); @@ -1066,15 +1128,17 @@ class _ParchmentHtmlDecoder extends Converter { } else if (element.localName == 'ol') { if (_hasList(updated)) { final indentLevel = updated.value(ParchmentAttribute.indent) ?? 0; - updated = - updated.put(ParchmentAttribute.indent.withLevel(indentLevel + 1)); + updated = updated.put( + ParchmentAttribute.indent.withLevel(indentLevel + 1), + ); } updated = updated.put(ParchmentAttribute.ol); } else if (element.localName == 'ul') { if (_hasList(updated)) { final indentLevel = updated.value(ParchmentAttribute.indent) ?? 0; - updated = - updated.put(ParchmentAttribute.indent.withLevel(indentLevel + 1)); + updated = updated.put( + ParchmentAttribute.indent.withLevel(indentLevel + 1), + ); } updated = updated.put(ParchmentAttribute.ul); } else if (element.localName == 'input' && diff --git a/packages/parchment/lib/src/codecs/html_utils.dart b/packages/parchment/lib/src/codecs/html_utils.dart index 4120b3e2..ef3d1fc7 100644 --- a/packages/parchment/lib/src/codecs/html_utils.dart +++ b/packages/parchment/lib/src/codecs/html_utils.dart @@ -3,7 +3,7 @@ A: (0xff000000 & colorValue) >> 24, R: (0x00ff0000 & colorValue) >> 16, G: (0x0000ff00 & colorValue) >> 8, - B: (0x000000ff & colorValue) >> 0 + B: (0x000000ff & colorValue) >> 0, ); } @@ -78,7 +78,8 @@ List _chooseSplit(String cssColor) { split = cssColor.split(' '); if (split.length < 3) { throw ArgumentError( - 'CSS color - rgb(a) must have at least 3 components. Received $cssColor'); + 'CSS color - rgb(a) must have at least 3 components. Received $cssColor', + ); } final clean = []; @@ -95,11 +96,13 @@ List _chooseSplit(String cssColor) { } if (hasSlash && clean.length != 4) { throw ArgumentError( - 'CSS color - expected 4 components. Received $cssColor'); + 'CSS color - expected 4 components. Received $cssColor', + ); } if (!hasSlash && clean.length != 3) { throw ArgumentError( - 'CSS color : expecting 3 components. Received $cssColor'); + 'CSS color : expecting 3 components. Received $cssColor', + ); } return clean; } diff --git a/packages/parchment/lib/src/codecs/markdown.dart b/packages/parchment/lib/src/codecs/markdown.dart index 18e141ae..12bc9cb3 100644 --- a/packages/parchment/lib/src/codecs/markdown.dart +++ b/packages/parchment/lib/src/codecs/markdown.dart @@ -78,10 +78,18 @@ class _ParchmentMarkdownDecoder extends Converter { if (style?.isInline ?? true) { _handleSpan(line, delta, true, style); } else { - _handleSpan(line, delta, false, - ParchmentStyle().putAll(style?.inlineAttributes ?? [])); - _handleSpan('\n', delta, false, - ParchmentStyle().putAll(style?.lineAttributes ?? [])); + _handleSpan( + line, + delta, + false, + ParchmentStyle().putAll(style?.inlineAttributes ?? []), + ); + _handleSpan( + '\n', + delta, + false, + ParchmentStyle().putAll(style?.lineAttributes ?? []), + ); } } } @@ -141,8 +149,9 @@ class _ParchmentMarkdownDecoder extends Converter { _handleSpan(span, delta, false, style); ParchmentStyle blockStyle = ParchmentStyle().put(ParchmentAttribute.ol); if (indent > 0) { - blockStyle = - blockStyle.put(ParchmentAttribute.indent.withLevel(indent)); + blockStyle = blockStyle.put( + ParchmentAttribute.indent.withLevel(indent), + ); } _handleSpan('\n', delta, false, blockStyle); return true; @@ -161,10 +170,18 @@ class _ParchmentMarkdownDecoder extends Converter { final match = _ulRegExp.matchAsPrefix(line); final span = match?.group(2); if (span != null) { - _handleSpan(span, delta, false, - ParchmentStyle().putAll(newStyle.inlineAttributes)); _handleSpan( - '\n', delta, false, ParchmentStyle().putAll(newStyle.lineAttributes)); + span, + delta, + false, + ParchmentStyle().putAll(newStyle.inlineAttributes), + ); + _handleSpan( + '\n', + delta, + false, + ParchmentStyle().putAll(newStyle.lineAttributes), + ); return true; } return false; @@ -176,8 +193,9 @@ class _ParchmentMarkdownDecoder extends Converter { return false; } - ParchmentStyle newStyle = - (style ?? ParchmentStyle()).put(ParchmentAttribute.cl); + ParchmentStyle newStyle = (style ?? ParchmentStyle()).put( + ParchmentAttribute.cl, + ); final match = _clRegExp.matchAsPrefix(line); final span = match?.group(3); @@ -186,10 +204,18 @@ class _ParchmentMarkdownDecoder extends Converter { newStyle = newStyle.put(ParchmentAttribute.checked); } if (span != null) { - _handleSpan(span, delta, false, - ParchmentStyle().putAll(newStyle.inlineAttributes)); _handleSpan( - '\n', delta, false, ParchmentStyle().putAll(newStyle.lineAttributes)); + span, + delta, + false, + ParchmentStyle().putAll(newStyle.inlineAttributes), + ); + _handleSpan( + '\n', + delta, + false, + ParchmentStyle().putAll(newStyle.lineAttributes), + ); return true; } return false; @@ -200,17 +226,26 @@ class _ParchmentMarkdownDecoder extends Converter { final levelTag = match?.group(1); if (levelTag != null) { final level = levelTag.length; - final newStyle = (style ?? ParchmentStyle()) - .put(ParchmentAttribute.heading.withValue(level)); + final newStyle = (style ?? ParchmentStyle()).put( + ParchmentAttribute.heading.withValue(level), + ); final span = match?.group(2); if (span == null) { return false; } - _handleSpan(span, delta, false, - ParchmentStyle().putAll(newStyle.inlineAttributes)); _handleSpan( - '\n', delta, false, ParchmentStyle().putAll(newStyle.lineAttributes)); + span, + delta, + false, + ParchmentStyle().putAll(newStyle.inlineAttributes), + ); + _handleSpan( + '\n', + delta, + false, + ParchmentStyle().putAll(newStyle.lineAttributes), + ); return true; } @@ -218,7 +253,11 @@ class _ParchmentMarkdownDecoder extends Converter { } void _handleSpan( - String span, Delta delta, bool addNewLine, ParchmentStyle? outerStyle) { + String span, + Delta delta, + bool addNewLine, + ParchmentStyle? outerStyle, + ) { var start = _handleStyles(span, delta, outerStyle); span = span.substring(start); @@ -255,14 +294,18 @@ class _ParchmentMarkdownDecoder extends Converter { if (match.start > start) { if (span.substring(match.start - 1, match.start) == '[') { delta.insert( - span.substring(start, match.start - 1), outerStyle?.toJson()); + span.substring(start, match.start - 1), + outerStyle?.toJson(), + ); start = match.start - 1 + _handleLinks(span.substring(match.start - 1), delta, outerStyle); continue; } else { delta.insert( - span.substring(start, match.start), outerStyle?.toJson()); + span.substring(start, match.start), + outerStyle?.toJson(), + ); } } @@ -300,19 +343,20 @@ class _ParchmentMarkdownDecoder extends Converter { ParchmentStyle _fromStyleTag(String styleTag) { assert( - (styleTag == '`') | - (styleTag == '~~') | - (styleTag == '_') | - (styleTag == '*') | - (styleTag == '__') | - (styleTag == '**') | - (styleTag == '__*') | - (styleTag == '**_') | - (styleTag == '_**') | - (styleTag == '*__') | - (styleTag == '***') | - (styleTag == '___'), - 'Invalid style tag \'$styleTag\''); + (styleTag == '`') | + (styleTag == '~~') | + (styleTag == '_') | + (styleTag == '*') | + (styleTag == '__') | + (styleTag == '**') | + (styleTag == '__*') | + (styleTag == '**_') | + (styleTag == '_**') | + (styleTag == '*__') | + (styleTag == '***') | + (styleTag == '___'), + 'Invalid style tag \'$styleTag\'', + ); assert(styleTag.isNotEmpty, 'Style tag must not be empty'); if (styleTag == '`') { return ParchmentStyle().put(ParchmentAttribute.inlineCode); @@ -321,8 +365,10 @@ class _ParchmentMarkdownDecoder extends Converter { return ParchmentStyle().put(ParchmentAttribute.strikethrough); } if (styleTag.length == 3) { - return ParchmentStyle() - .putAll([ParchmentAttribute.bold, ParchmentAttribute.italic]); + return ParchmentStyle().putAll([ + ParchmentAttribute.bold, + ParchmentAttribute.italic, + ]); } if (styleTag.length == 2) { return ParchmentStyle().put(ParchmentAttribute.bold); @@ -344,8 +390,9 @@ class _ParchmentMarkdownDecoder extends Converter { if (text == null || href == null) { return start; } - final newStyle = (outerStyle ?? ParchmentStyle()) - .put(ParchmentAttribute.link.fromString(href)); + final newStyle = (outerStyle ?? ParchmentStyle()).put( + ParchmentAttribute.link.fromString(href), + ); _handleSpan(text, delta, false, newStyle); start = match.end; @@ -376,7 +423,10 @@ class _ParchmentMarkdownEncoder extends Converter { } void handleText( - StringBuffer buffer, TextNode node, ParchmentStyle currentInlineStyle) { + StringBuffer buffer, + TextNode node, + ParchmentStyle currentInlineStyle, + ) { final style = node.style; final rightPadding = _trimRight(buffer); @@ -459,9 +509,9 @@ class _ParchmentMarkdownEncoder extends Converter { lineBuffer.write(currentItemOrders[currentLevel]); } else if (node.style.containsSame(ParchmentAttribute.cl)) { lineBuffer.write('- ['); - if ((lineNode as LineNode) - .style - .contains(ParchmentAttribute.checked)) { + if ((lineNode as LineNode).style.contains( + ParchmentAttribute.checked, + )) { lineBuffer.write('X'); } else { lineBuffer.write(' '); @@ -492,8 +542,11 @@ class _ParchmentMarkdownEncoder extends Converter { return buffer.toString(); } - void _writeAttribute(StringBuffer buffer, ParchmentAttribute? attribute, - {bool close = false}) { + void _writeAttribute( + StringBuffer buffer, + ParchmentAttribute? attribute, { + bool close = false, + }) { if (attribute == ParchmentAttribute.bold) { _writeBoldTag(buffer); } else if (attribute == ParchmentAttribute.italic) { @@ -503,13 +556,19 @@ class _ParchmentMarkdownEncoder extends Converter { } else if (attribute == ParchmentAttribute.strikethrough) { _writeStrikeThoughTag(buffer); } else if (attribute?.key == ParchmentAttribute.link.key) { - _writeLinkTag(buffer, attribute as ParchmentAttribute, - close: close); + _writeLinkTag( + buffer, + attribute as ParchmentAttribute, + close: close, + ); } else if (attribute?.key == ParchmentAttribute.heading.key) { _writeHeadingTag(buffer, attribute as ParchmentAttribute); } else if (attribute?.key == ParchmentAttribute.block.key) { - _writeBlockTag(buffer, attribute as ParchmentAttribute, - close: close); + _writeBlockTag( + buffer, + attribute as ParchmentAttribute, + close: close, + ); } else if (attribute?.key == ParchmentAttribute.checked.key) { // no-op already handled in handleBlock } else if (attribute?.key == ParchmentAttribute.indent.key) { @@ -535,8 +594,11 @@ class _ParchmentMarkdownEncoder extends Converter { buffer.write('~~'); } - void _writeLinkTag(StringBuffer buffer, ParchmentAttribute link, - {bool close = false}) { + void _writeLinkTag( + StringBuffer buffer, + ParchmentAttribute link, { + bool close = false, + }) { if (close) { buffer.write('](${link.value})'); } else { @@ -549,8 +611,11 @@ class _ParchmentMarkdownEncoder extends Converter { buffer.write('${'#' * level} '); } - void _writeBlockTag(StringBuffer buffer, ParchmentAttribute block, - {bool close = false}) { + void _writeBlockTag( + StringBuffer buffer, + ParchmentAttribute block, { + bool close = false, + }) { if (block == ParchmentAttribute.code) { if (close) { buffer.write('\n```'); diff --git a/packages/parchment/lib/src/document.dart b/packages/parchment/lib/src/document.dart index c3aee4b9..b35e11de 100644 --- a/packages/parchment/lib/src/document.dart +++ b/packages/parchment/lib/src/document.dart @@ -38,25 +38,27 @@ class ParchmentChange { /// A rich text document. class ParchmentDocument { /// Creates new empty Parchment document. - ParchmentDocument( - {ParchmentHeuristics heuristics = ParchmentHeuristics.fallback}) - : _heuristics = heuristics, + ParchmentDocument({ + ParchmentHeuristics heuristics = ParchmentHeuristics.fallback, + }) : _heuristics = heuristics, _delta = Delta()..insert('\n') { _loadDocument(_delta); } /// Creates new ParchmentDocument from provided JSON `data`. - ParchmentDocument.fromJson(List data, - {ParchmentHeuristics heuristics = ParchmentHeuristics.fallback}) - : _heuristics = heuristics, + ParchmentDocument.fromJson( + List data, { + ParchmentHeuristics heuristics = ParchmentHeuristics.fallback, + }) : _heuristics = heuristics, _delta = _migrateDelta(Delta.fromJson(data)) { _loadDocument(_delta); } /// Creates new ParchmentDocument from provided `delta`. - ParchmentDocument.fromDelta(Delta delta, - {ParchmentHeuristics heuristics = ParchmentHeuristics.fallback}) - : _heuristics = heuristics, + ParchmentDocument.fromDelta( + Delta delta, { + ParchmentHeuristics heuristics = ParchmentHeuristics.fallback, + }) : _heuristics = heuristics, _delta = _migrateDelta(delta) { _loadDocument(_delta); } @@ -148,8 +150,10 @@ class ParchmentDocument { final dataIsNotEmpty = (data is String) ? data.isNotEmpty : true; - assert(index >= 0 && (dataIsNotEmpty || length > 0), - 'With index $index, length $length and text "$data"'); + assert( + index >= 0 && (dataIsNotEmpty || length > 0), + 'With index $index, length $length and text "$data"', + ); var delta = Delta(); @@ -180,8 +184,12 @@ class ParchmentDocument { var change = Delta(); - final formatChange = - _heuristics.applyFormatRules(this, index, length, attribute); + final formatChange = _heuristics.applyFormatRules( + this, + index, + length, + attribute, + ); if (formatChange.isNotEmpty) { compose(formatChange, ChangeSource.local); change = change.compose(formatChange); @@ -251,8 +259,10 @@ class ParchmentDocument { _delta = _delta.compose(change); if (_delta != _root.toDelta()) { - throw StateError('Compose produced inconsistent results. ' - 'This is likely due to a bug in the library. Tried to compose change $change from $source.'); + throw StateError( + 'Compose produced inconsistent results. ' + 'This is likely due to a bug in the library. Tried to compose change $change from $source.', + ); } _controller.add(ParchmentChange(before, change, source)); } @@ -268,8 +278,10 @@ class ParchmentDocument { // void _checkMutable() { - assert(!_controller.isClosed, - 'Cannot modify Parchment document after it was closed.'); + assert( + !_controller.isClosed, + 'Cannot modify Parchment document after it was closed.', + ); } /// Key of the embed attribute used in Parchment 0.x (prior to 1.0). @@ -308,8 +320,10 @@ class ParchmentDocument { /// Loads [document] delta into this document. void _loadDocument(Delta doc) { - assert((doc.last.data as String).endsWith('\n'), - 'Invalid document delta. Document delta must always end with a line-break.'); + assert( + (doc.last.data as String).endsWith('\n'), + 'Invalid document delta. Document delta must always end with a line-break.', + ); var offset = 0; for (final op in doc.toList()) { final style = @@ -318,8 +332,10 @@ class ParchmentDocument { final data = _normalizeData(op.data); _root.insert(offset, data, style); } else { - throw ArgumentError.value(doc, - 'Document Delta can only contain insert operations but ${op.key} found.'); + throw ArgumentError.value( + doc, + 'Document Delta can only contain insert operations but ${op.key} found.', + ); } offset += op.length; } diff --git a/packages/parchment/lib/src/document/attributes.dart b/packages/parchment/lib/src/document/attributes.dart index 120ee711..040e03f9 100644 --- a/packages/parchment/lib/src/document/attributes.dart +++ b/packages/parchment/lib/src/document/attributes.dart @@ -200,7 +200,9 @@ class ParchmentAttribute implements ParchmentAttributeBuilder { static ParchmentAttribute _fromKeyValue(String key, dynamic value) { if (!_registry.containsKey(key)) { throw ArgumentError.value( - key, 'No attribute with key "$key" registered.'); + key, + 'No attribute with key "$key" registered.', + ); } final builder = _registry[key]!; return builder.withValue(value); @@ -383,8 +385,10 @@ class ParchmentStyle { /// Returns JSON-serializable representation of this style. Map? toJson() => _data.isEmpty ? null - : _data.map((String _, ParchmentAttribute value) => - MapEntry(value.key, value.value)); + : _data.map( + (String _, ParchmentAttribute value) => + MapEntry(value.key, value.value), + ); @override bool operator ==(Object other) { @@ -396,8 +400,9 @@ class ParchmentStyle { @override int get hashCode { - final hashes = - _data.entries.map((entry) => Object.hash(entry.key, entry.value)); + final hashes = _data.entries.map( + (entry) => Object.hash(entry.key, entry.value), + ); return Object.hashAll(hashes); } @@ -638,6 +643,9 @@ class IndentAttributeBuilder extends ParchmentAttributeBuilder { return unset; } return ParchmentAttribute._( - key, scope, math.min(_maxIndentationLevel, level)); + key, + scope, + math.min(_maxIndentationLevel, level), + ); } } diff --git a/packages/parchment/lib/src/document/embeds.dart b/packages/parchment/lib/src/document/embeds.dart index aa05f311..7e46f7c4 100644 --- a/packages/parchment/lib/src/document/embeds.dart +++ b/packages/parchment/lib/src/document/embeds.dart @@ -19,10 +19,14 @@ class EmbeddableObject { this.type, { required this.inline, Map data = const {}, - }) : assert(!data.containsKey(kTypeKey), - 'The "$kTypeKey" key is reserved in $EmbeddableObject data and cannot be used.'), - assert(!data.containsKey(kInlineKey), - 'The "$kInlineKey" key is reserved in $EmbeddableObject data and cannot be used.'), + }) : assert( + !data.containsKey(kTypeKey), + 'The "$kTypeKey" key is reserved in $EmbeddableObject data and cannot be used.', + ), + assert( + !data.containsKey(kInlineKey), + 'The "$kInlineKey" key is reserved in $EmbeddableObject data and cannot be used.', + ), _data = Map.from(data); /// The type of this object. @@ -61,8 +65,9 @@ class EmbeddableObject { int get hashCode { if (_data.isEmpty) return Object.hash(type, inline); - final dataHash = - Object.hashAll(_data.entries.map((e) => Object.hash(e.key, e.value))); + final dataHash = Object.hashAll( + _data.entries.map((e) => Object.hash(e.key, e.value)), + ); return Object.hash(type, inline, dataHash); } @@ -76,10 +81,7 @@ class EmbeddableObject { /// An object which can be embedded on the same line (inline) with regular text. class SpanEmbed extends EmbeddableObject { - SpanEmbed( - super.type, { - super.data, - }) : super(inline: true); + SpanEmbed(super.type, {super.data}) : super(inline: true); } /// An object which occupies an entire line in a document and cannot co-exist @@ -92,10 +94,7 @@ class SpanEmbed extends EmbeddableObject { /// of embedded objects and allows users to define their own types. class BlockEmbed extends EmbeddableObject { /// Creates a new block embed of specified [type] and containing [data]. - BlockEmbed( - super.type, { - super.data, - }) : super(inline: false); + BlockEmbed(super.type, {super.data}) : super(inline: false); static final BlockEmbed horizontalRule = BlockEmbed('hr'); diff --git a/packages/parchment/lib/src/document/leaf.dart b/packages/parchment/lib/src/document/leaf.dart index dc5cbc1d..dd305386 100644 --- a/packages/parchment/lib/src/document/leaf.dart +++ b/packages/parchment/lib/src/document/leaf.dart @@ -93,9 +93,10 @@ abstract base class LeafNode extends Node with StyledNode { /// if provided [index] is `0`. LeafNode isolate(int index, int length) { assert( - index >= 0 && index < this.length && (index + length <= this.length), - 'Index or length is out of bounds. Index: $index, length: $length. ' - 'Actual node length: ${this.length}.'); + index >= 0 && index < this.length && (index + length <= this.length), + 'Index or length is out of bounds. Index: $index, length: $length. ' + 'Actual node length: ${this.length}.', + ); // Since `index < this.length` (guarded by assert) below line // always returns a new node. final target = splitAt(index)!; @@ -113,8 +114,10 @@ abstract base class LeafNode extends Node with StyledNode { @override void applyStyle(ParchmentStyle value) { - assert(value.isInline || value.isEmpty, - 'Style cannot be applied to this leaf node: $value'); + assert( + value.isInline || value.isEmpty, + 'Style cannot be applied to this leaf node: $value', + ); super.applyStyle(value); } @@ -139,8 +142,10 @@ abstract base class LeafNode extends Node with StyledNode { @override void insert(int index, Object data, ParchmentStyle? style) { - assert(index >= 0 && (index <= length), - 'Index out of bounds. Must be between 0 and $length, but got $index.'); + assert( + index >= 0 && (index <= length), + 'Index out of bounds. Must be between 0 and $length, but got $index.', + ); final node = LeafNode(data); if (index == length) { insertAfter(node); diff --git a/packages/parchment/lib/src/document/line.dart b/packages/parchment/lib/src/document/line.dart index 752149ff..1a763c14 100644 --- a/packages/parchment/lib/src/document/line.dart +++ b/packages/parchment/lib/src/document/line.dart @@ -21,8 +21,12 @@ final class LineNode extends ContainerNode with StyledNode { if (childCount == 1 && children.single is EmbedNode) { return !(children.single as EmbedNode).value.inline; } - assert(children.every((child) => - child is TextNode || (child is EmbedNode && child.value.inline))); + assert( + children.every( + (child) => + child is TextNode || (child is EmbedNode && child.value.inline), + ), + ); return false; } @@ -67,8 +71,10 @@ final class LineNode extends ContainerNode with StyledNode { /// /// This is an equivalent of inserting a line-break character at [index]. LineNode splitAt(int index) { - assert(index == 0 || (index > 0 && index < length), - 'Index is out of bounds. Index: $index. Actual node length: $length.'); + assert( + index == 0 || (index > 0 && index < length), + 'Index is out of bounds. Index: $index. Actual node length: $length.', + ); final line = clone(); insertAfter(line); @@ -248,16 +254,23 @@ final class LineNode extends ContainerNode with StyledNode { if (isLineFormat) { assert( - style.values - .every((attr) => attr.scope == ParchmentAttributeScope.line), - 'It is not allowed to apply inline attributes to line itself.'); + style.values.every( + (attr) => attr.scope == ParchmentAttributeScope.line, + ), + 'It is not allowed to apply inline attributes to line itself.', + ); _formatAndOptimize(style); } else { // Otherwise forward to children as it's an inline format update. - assert(index + local != thisLength, - 'It is not allowed to apply inline attributes to line itself.'); - assert(style.values - .every((attr) => attr.scope == ParchmentAttributeScope.inline)); + assert( + index + local != thisLength, + 'It is not allowed to apply inline attributes to line itself.', + ); + assert( + style.values.every( + (attr) => attr.scope == ParchmentAttributeScope.inline, + ), + ); super.retain(index, local, style); } @@ -321,8 +334,9 @@ final class LineNode extends ContainerNode with StyledNode { final blockStyle = newStyle.get(ParchmentAttribute.block)!; if (parent is BlockNode) { - final parentStyle = - (parent as BlockNode).style.get(ParchmentAttribute.block); + final parentStyle = (parent as BlockNode).style.get( + ParchmentAttribute.block, + ); if (blockStyle == ParchmentAttribute.block.unset) { unwrap(); } else if (blockStyle != parentStyle) { diff --git a/packages/parchment/lib/src/document/node.dart b/packages/parchment/lib/src/document/node.dart index d3e4544a..e26ea2cb 100644 --- a/packages/parchment/lib/src/document/node.dart +++ b/packages/parchment/lib/src/document/node.dart @@ -267,8 +267,10 @@ abstract base class ContainerNode extends Node { /// Content length of this node's children. To get number of children in this /// node use [childCount]. @override - int get length => _length ??= - _children.fold(0, (current, node) => current + node.length); + int get length => _length ??= _children.fold( + 0, + (current, node) => current + node.length, + ); @override void insert(int index, Object data, ParchmentStyle? style) { diff --git a/packages/parchment/lib/src/heuristics.dart b/packages/parchment/lib/src/heuristics.dart index afa9c609..0dc210ac 100644 --- a/packages/parchment/lib/src/heuristics.dart +++ b/packages/parchment/lib/src/heuristics.dart @@ -70,8 +70,12 @@ class ParchmentHeuristics { /// Applies heuristic rules to specified format operation based on current /// state of Parchment [document]. - Delta applyFormatRules(ParchmentDocument document, int index, int length, - ParchmentAttribute value) { + Delta applyFormatRules( + ParchmentDocument document, + int index, + int length, + ParchmentAttribute value, + ) { final delta = document.toDelta(); for (var rule in formatRules) { final result = rule.apply(delta, index, length, value); diff --git a/packages/parchment/lib/src/heuristics/delete_rules.dart b/packages/parchment/lib/src/heuristics/delete_rules.dart index 2cace65d..b81fdb5b 100644 --- a/packages/parchment/lib/src/heuristics/delete_rules.dart +++ b/packages/parchment/lib/src/heuristics/delete_rules.dart @@ -94,7 +94,8 @@ class PreserveLineStyleOnMergeRule extends DeleteRule { Map? _unsetAttributes(Map? attributes) { if (attributes == null) return null; return attributes.map( - (String key, dynamic value) => MapEntry(key, null)); + (String key, dynamic value) => MapEntry(key, null), + ); } } diff --git a/packages/parchment/lib/src/heuristics/format_rules.dart b/packages/parchment/lib/src/heuristics/format_rules.dart index 6e48e64b..8ab974d1 100644 --- a/packages/parchment/lib/src/heuristics/format_rules.dart +++ b/packages/parchment/lib/src/heuristics/format_rules.dart @@ -10,7 +10,11 @@ abstract class FormatRule { /// Applies heuristic rule to a retain (format) operation on a [document] and /// returns resulting [Delta]. Delta? apply( - Delta document, int index, int length, ParchmentAttribute attribute); + Delta document, + int index, + int length, + ParchmentAttribute attribute, + ); } /// Produces Delta with line-level attributes applied strictly to @@ -20,7 +24,11 @@ class ResolveLineFormatRule extends FormatRule { @override Delta? apply( - Delta document, int index, int length, ParchmentAttribute attribute) { + Delta document, + int index, + int length, + ParchmentAttribute attribute, + ) { if (attribute.scope != ParchmentAttributeScope.line) return null; var result = Delta()..retain(index); @@ -57,8 +65,12 @@ class ResolveLineFormatRule extends FormatRule { return result; } - Delta _applyAttribute(String text, Operation op, ParchmentAttribute attribute, - {bool firstOnly = false}) { + Delta _applyAttribute( + String text, + Operation op, + ParchmentAttribute attribute, { + bool firstOnly = false, + }) { final result = Delta(); var offset = 0; var lf = text.indexOf('\n'); @@ -97,7 +109,11 @@ class ResolveInlineFormatRule extends FormatRule { @override Delta? apply( - Delta document, int index, int length, ParchmentAttribute attribute) { + Delta document, + int index, + int length, + ParchmentAttribute attribute, + ) { if (attribute.scope != ParchmentAttributeScope.inline) return null; final result = Delta()..retain(index); @@ -137,7 +153,11 @@ class FormatLinkAtCaretPositionRule extends FormatRule { @override Delta? apply( - Delta document, int index, int length, ParchmentAttribute attribute) { + Delta document, + int index, + int length, + ParchmentAttribute attribute, + ) { if (attribute.key != ParchmentAttribute.link.key) return null; // If user selection is not collapsed we let it fallback to default rule // which simply applies the attribute to selected range. diff --git a/packages/parchment/lib/src/heuristics/insert_rules.dart b/packages/parchment/lib/src/heuristics/insert_rules.dart index f549ba08..0a166d10 100644 --- a/packages/parchment/lib/src/heuristics/insert_rules.dart +++ b/packages/parchment/lib/src/heuristics/insert_rules.dart @@ -359,7 +359,7 @@ class PreserveBlockStyleOnInsertRule extends InsertRule { if (!lineStyle.containsKey(ParchmentAttribute.block.key)) return null; final blockStyle = { - ParchmentAttribute.block.key: lineStyle[ParchmentAttribute.block.key] + ParchmentAttribute.block.key: lineStyle[ParchmentAttribute.block.key], }; Map resetStyle = {}; @@ -447,7 +447,9 @@ class InsertBlockEmbedsRule extends InsertRule { } Map? _getLineStyle( - DeltaIterator iterator, Operation current) { + DeltaIterator iterator, + Operation current, + ) { final currentText = current.data is String ? current.data as String : ''; if (currentText.contains('\n')) { diff --git a/packages/parchment/test/codecs/html_test.dart b/packages/parchment/test/codecs/html_test.dart index 410c0a1b..43948185 100644 --- a/packages/parchment/test/codecs/html_test.dart +++ b/packages/parchment/test/codecs/html_test.dart @@ -16,7 +16,7 @@ void main() { test('plain text', () { final doc = ParchmentDocument.fromJson([ - {'insert': 'Something in the way mmmm...\n'} + {'insert': 'Something in the way mmmm...\n'}, ]); expect(codec.encode(doc), 'Something in the way mmmm...'); }); @@ -26,12 +26,14 @@ void main() { {'insert': 'Something '}, { 'insert': 'in the way', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, - {'insert': ' mmmm...\n'} + {'insert': ' mmmm...\n'}, ]); expect( - codec.encode(doc), 'Something in the way mmmm...'); + codec.encode(doc), + 'Something in the way mmmm...', + ); }); test('background color', () { @@ -39,12 +41,14 @@ void main() { {'insert': 'Something '}, { 'insert': 'in the way', - 'attributes': {'bg': 0xFFFF0000} + 'attributes': {'bg': 0xFFFF0000}, }, - {'insert': ' mmmm...\n'} + {'insert': ' mmmm...\n'}, ]); - expect(codec.encode(doc), - 'Something in the way mmmm...'); + expect( + codec.encode(doc), + 'Something in the way mmmm...', + ); }); test('text color', () { @@ -52,52 +56,58 @@ void main() { {'insert': 'Something '}, { 'insert': 'in the way', - 'attributes': {'fg': 0xFFFF0000} + 'attributes': {'fg': 0xFFFF0000}, }, - {'insert': ' mmmm...\n'} + {'insert': ' mmmm...\n'}, ]); - expect(codec.encode(doc), - 'Something in the way mmmm...'); + expect( + codec.encode(doc), + 'Something in the way mmmm...', + ); }); test('italic + code + underlined + strikethrough text', () { final doc = ParchmentDocument.fromJson([ { 'insert': 'Something ', - 'attributes': {'s': true, 'u': true} + 'attributes': {'s': true, 'u': true}, }, { 'insert': 'in the way', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, { 'insert': ' mmmm...', - 'attributes': {'c': true} + 'attributes': {'c': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); - expect(codec.encode(doc), - 'Something in the way mmmm...'); + expect( + codec.encode(doc), + 'Something in the way mmmm...', + ); }); test('embedded inline attributes text', () { final doc = ParchmentDocument.fromJson([ { 'insert': 'Something ', - 'attributes': {'a': 'https://wikipedia.org', 'u': true} + 'attributes': {'a': 'https://wikipedia.org', 'u': true}, }, { 'insert': 'in the way', - 'attributes': {'i': true, 'u': true} + 'attributes': {'i': true, 'u': true}, }, { 'insert': ' mmmm...', - 'attributes': {'u': true} + 'attributes': {'u': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); - expect(codec.encode(doc), - 'Something in the way mmmm...'); + expect( + codec.encode(doc), + 'Something in the way mmmm...', + ); }); test('tangled inline tags', () { @@ -105,20 +115,22 @@ void main() { {'insert': 'AAA'}, { 'insert': 'BB', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': 'B', - 'attributes': {'b': true, 's': true} + 'attributes': {'b': true, 's': true}, }, { 'insert': 'CCC', - 'attributes': {'s': true} + 'attributes': {'s': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); - expect(codec.encode(doc), - 'AAABBBCCC'); + expect( + codec.encode(doc), + 'AAABBBCCC', + ); }); test('html escaping', () { @@ -128,34 +140,37 @@ void main() { 'HTML special characters like < > & are escaped, but not \' " /.\n', }, ]); - expect(codec.encode(doc), - 'HTML special characters like < > & are escaped, but not \' " /.'); + expect( + codec.encode(doc), + 'HTML special characters like < > & are escaped, but not \' " /.', + ); }); - test('multiple line breaks in a row should render as actual line breaks', - () { - // This has three blank lines between the Line 1/Line2 pair. - // The Line3/Line4 pair does not have blank lines, but both pairs should render to the - // same height. The Line5/Line6 pair has 3 blank lines but also were emboldened in Fleather. - // The blank line after Line5 has a space in it just to distinguish it from a completely - // blank line. - final doc = ParchmentDocument.fromJson([ - { - 'insert': - 'Line 1\n\n\n\nLine 2\nLine3\nnot blank1\nnot blank2\nnot blank3\nLine 4\n' - }, - { - 'insert': 'Line 5', - 'attributes': {'b': true} - }, - {'insert': '\n \n\n\n'}, - { - 'insert': 'Line 6', - 'attributes': {'b': true} - }, - {'insert': '\n'} - ]); - expect( + test( + 'multiple line breaks in a row should render as actual line breaks', + () { + // This has three blank lines between the Line 1/Line2 pair. + // The Line3/Line4 pair does not have blank lines, but both pairs should render to the + // same height. The Line5/Line6 pair has 3 blank lines but also were emboldened in Fleather. + // The blank line after Line5 has a space in it just to distinguish it from a completely + // blank line. + final doc = ParchmentDocument.fromJson([ + { + 'insert': + 'Line 1\n\n\n\nLine 2\nLine3\nnot blank1\nnot blank2\nnot blank3\nLine 4\n', + }, + { + 'insert': 'Line 5', + 'attributes': {'b': true}, + }, + {'insert': '\n \n\n\n'}, + { + 'insert': 'Line 6', + 'attributes': {'b': true}, + }, + {'insert': '\n'}, + ]); + expect( codec.encode(doc), '

Line 1

' '


' @@ -171,45 +186,48 @@ void main() { '


' '


' '


' - '

Line 6

'); - }); + '

Line 6

', + ); + }, + ); test('several styled lines in a row', () { // Tests that we don't generate nested

tags. final doc = ParchmentDocument.fromJson([ { 'insert': 'Bold', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': '\n'}, { 'insert': 'Italic', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, {'insert': '\n'}, { 'insert': 'Bold', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': '\n'}, { 'insert': 'Italic', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, {'insert': '\n'}, { 'insert': 'Bold', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': '\n'}, ]); expect( - codec.encode(doc), - '

Bold

' - '

Italic

' - '

Bold

' - '

Italic

' - '

Bold

'); + codec.encode(doc), + '

Bold

' + '

Italic

' + '

Bold

' + '

Italic

' + '

Bold

', + ); }); }); @@ -219,7 +237,7 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 1} + 'attributes': {'heading': 1}, }, ]); @@ -231,8 +249,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 2} - } + 'attributes': {'heading': 2}, + }, ]); expect(codec.encode(doc), '

Hello World!

'); @@ -243,8 +261,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 3} - } + 'attributes': {'heading': 3}, + }, ]); expect(codec.encode(doc), '

Hello World!

'); @@ -255,8 +273,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 4} - } + 'attributes': {'heading': 4}, + }, ]); expect(codec.encode(doc), '

Hello World!

'); @@ -267,8 +285,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 5} - } + 'attributes': {'heading': 5}, + }, ]); expect(codec.encode(doc), '
Hello World!
'); @@ -279,8 +297,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 6} - } + 'attributes': {'heading': 6}, + }, ]); expect(codec.encode(doc), '
Hello World!
'); @@ -300,17 +318,19 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': '\n'}, { 'insert': 'Bye World!', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); - expect(codec.encode(doc), - '

Hello World!

Bye World!

'); + expect( + codec.encode(doc), + '

Hello World!

Bye World!

', + ); }); }); @@ -320,12 +340,14 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} - } + 'attributes': {'block': 'quote'}, + }, ]); - expect(codec.encode(doc), - '
Hello World!
'); + expect( + codec.encode(doc), + '
Hello World!
', + ); }); test('Consecutive with same style', () { @@ -333,19 +355,20 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} - } + 'attributes': {'block': 'quote'}, + }, ]); expect( - codec.encode(doc), - '
Hello World!
' - '
Hello World!
'); + codec.encode(doc), + '
Hello World!
' + '
Hello World!
', + ); }); test('Consecutive with different styles', () { @@ -353,19 +376,20 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote', 'alignment': 'center'} - } + 'attributes': {'block': 'quote', 'alignment': 'center'}, + }, ]); expect( - codec.encode(doc), - '
Hello World!
' - '
Hello World!
'); + codec.encode(doc), + '
Hello World!
' + '
Hello World!
', + ); }); }); @@ -374,18 +398,18 @@ void main() { {'insert': 'void main() {'}, { 'insert': '\n\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': ' print("Hello World!");'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': '}'}, { 'insert': '\n', - 'attributes': {'block': 'code'} - } + 'attributes': {'block': 'code'}, + }, ]); expect( @@ -403,7 +427,7 @@ void main() { {'insert': 'some code'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': 'Hello world\n'}, ]); @@ -419,13 +443,13 @@ void main() { {'insert': 'some code'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, { 'insert': 'Hello world', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect( codec.encode(doc), @@ -439,17 +463,17 @@ void main() { {'insert': 'Hello world\n'}, { 'insert': 'Another', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': ' one\n'}, {'insert': 'some '}, { 'insert': 'quote', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, ]); expect( @@ -464,16 +488,16 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': - 'Hello world\nHello world\nHello world\nHello world\nsome code' + 'Hello world\nHello world\nHello world\nHello world\nsome code', }, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': 'Hello world\nsome quote'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, {'insert': 'Hello world\n'}, ]); @@ -495,38 +519,42 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'This is Fleather!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} - } + 'attributes': {'block': 'ol'}, + }, ]); - expect(codec.encode(doc), - '
  1. Hello World!
  2. This is Fleather!
'); + expect( + codec.encode(doc), + '
  1. Hello World!
  2. This is Fleather!
', + ); }); test('List with bold', () { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'This is Fleather!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} - } + 'attributes': {'block': 'ol'}, + }, ]); - expect(codec.encode(doc), - '
  1. Hello World!
  2. This is Fleather!
'); + expect( + codec.encode(doc), + '
  1. Hello World!
  2. This is Fleather!
', + ); }); test('Unordered list', () { @@ -534,17 +562,19 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'This is Fleather!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} - } + 'attributes': {'block': 'ul'}, + }, ]); - expect(codec.encode(doc), - '
  • Hello World!
  • This is Fleather!
'); + expect( + codec.encode(doc), + '
  • Hello World!
  • This is Fleather!
', + ); }); test('Successive list', () { @@ -552,23 +582,23 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'This is Fleather!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': '\nHello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'This is Fleather!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} - } + 'attributes': {'block': 'ul'}, + }, ]); expect( @@ -590,21 +620,22 @@ void main() { {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'cl', 'checked': true} + 'attributes': {'block': 'cl', 'checked': true}, }, {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'cl'} - } + 'attributes': {'block': 'cl'}, + }, ]); expect( - codec.encode(doc), - '
' - '
' - '
' - '
'); + codec.encode(doc), + '
' + '
' + '
' + '
', + ); }); test('Checklist followed by a link', () { @@ -612,28 +643,29 @@ void main() { {'insert': 'Check - 1'}, { 'insert': '\n', - 'attributes': {'block': 'cl', 'checked': true} + 'attributes': {'block': 'cl', 'checked': true}, }, {'insert': 'Check - 2'}, { 'insert': '\n', - 'attributes': {'block': 'cl'} + 'attributes': {'block': 'cl'}, }, {'insert': 'A link to a '}, { 'insert': 'site', - 'attributes': {'a': 'https://example.com'} + 'attributes': {'a': 'https://example.com'}, }, - {'insert': '.\n'} + {'insert': '.\n'}, ]); expect( - codec.encode(doc), - '
' - '
' - '
' - '
' - '

A link to a site.

'); + codec.encode(doc), + '
' + '
' + '
' + '
' + '

A link to a site.

', + ); }); test('Checklist followed by a paragraph', () { @@ -641,22 +673,23 @@ void main() { {'insert': 'Check - 1'}, { 'insert': '\n', - 'attributes': {'block': 'cl', 'checked': true} + 'attributes': {'block': 'cl', 'checked': true}, }, {'insert': 'Check - 2'}, { 'insert': '\n', - 'attributes': {'block': 'cl'} + 'attributes': {'block': 'cl'}, }, {'insert': 'Paragraph\n'}, ]); expect( - codec.encode(doc), - '
' - '
' - '
' - '

Paragraph

'); + codec.encode(doc), + '
' + '
' + '
' + '

Paragraph

', + ); }); }); @@ -667,11 +700,13 @@ void main() { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link'}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect( - codec.encode(doc), 'Hello World!'); + codec.encode(doc), + 'Hello World!', + ); }); test('Italic', () { @@ -680,11 +715,13 @@ void main() { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link', 'i': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); - expect(codec.encode(doc), - 'Hello World!'); + expect( + codec.encode(doc), + 'Hello World!', + ); }); test('In list', () { @@ -696,11 +733,13 @@ void main() { { 'insert': '\n', 'attributes': {'block': 'ul'}, - } + }, ]); - expect(codec.encode(doc), - ''); + expect( + codec.encode(doc), + '', + ); }); }); @@ -712,12 +751,14 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'direction': 'rtl'} - } + 'attributes': {'direction': 'rtl'}, + }, ]); - expect(codec.encode(doc), - '

Hello World!

Hello World!

'); + expect( + codec.encode(doc), + '

Hello World!

Hello World!

', + ); }); test('In list', () { @@ -733,13 +774,15 @@ void main() { 'attributes': { 'direction': 'rtl', 'block': 'ol', - 'alignment': 'center' + 'alignment': 'center', }, }, ]); - expect(codec.encode(doc), - '
  1. Hello World!
  2. Hello World!
'); + expect( + codec.encode(doc), + '
  1. Hello World!
  2. Hello World!
', + ); }); }); @@ -749,11 +792,13 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'center'} - } + 'attributes': {'alignment': 'center'}, + }, ]); - expect(codec.encode(doc), - '

Hello World!

'); + expect( + codec.encode(doc), + '

Hello World!

', + ); }); test('all paragraph alignments', () { @@ -761,23 +806,23 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': null} + 'attributes': {'alignment': null}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'right'} + 'attributes': {'alignment': 'right'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'center'} + 'attributes': {'alignment': 'center'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'justify'} - } + 'attributes': {'alignment': 'justify'}, + }, ]); expect( codec.encode(doc), @@ -793,32 +838,33 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': null} + 'attributes': {'block': 'ol', 'alignment': null}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'right'} + 'attributes': {'block': 'ol', 'alignment': 'right'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'center'} + 'attributes': {'block': 'ol', 'alignment': 'center'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'justify'} - } + 'attributes': {'block': 'ol', 'alignment': 'justify'}, + }, ]); expect( - codec.encode(doc), - '
    ' - '
  1. Hello World!
  2. ' - '
  3. Hello World!
  4. ' - '
  5. Hello World!
  6. ' - '
  7. Hello World!
  8. ' - '
'); + codec.encode(doc), + '
    ' + '
  1. Hello World!
  2. ' + '
  3. Hello World!
  4. ' + '
  5. Hello World!
  6. ' + '
  7. Hello World!
  8. ' + '
', + ); }); }); @@ -828,49 +874,50 @@ void main() { {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, ]); expect( - codec.encode(doc), - '
    ' - '
  1. item
  2. ' - '
      ' - '
    • sub-item
    • ' - '
        ' - '
      1. sub-sub-item
      2. ' - '
      3. sub-sub-item
      4. ' - '
      ' - '
    • sub-item
    • ' - '
    ' - '
  3. item
  4. ' - '
'); + codec.encode(doc), + '
    ' + '
  1. item
  2. ' + '
      ' + '
    • sub-item
    • ' + '
        ' + '
      1. sub-sub-item
      2. ' + '
      3. sub-sub-item
      4. ' + '
      ' + '
    • sub-item
    • ' + '
    ' + '
  3. item
  4. ' + '
', + ); }); test('Multiple nested lists - 4 levels', () { @@ -878,62 +925,62 @@ void main() { {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 2} + 'attributes': {'block': 'ul', 'indent': 2}, }, {'insert': 'sub-sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, {'insert': 'sub-sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 2} + 'attributes': {'block': 'ul', 'indent': 2}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, ]); @@ -972,27 +1019,29 @@ void main() { {'insert': 'Level 1 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 1 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 2 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Level 2 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, - {'insert': 'No longer in list\n'} + {'insert': 'No longer in list\n'}, ]); - expect(codec.encode(doc), - '

Test

  1. Level 1 - 1
  2. Level 1 - 2
    1. Level 2 - 1
    2. Level 2 - 2

No longer in list

'); + expect( + codec.encode(doc), + '

Test

  1. Level 1 - 1
  2. Level 1 - 2
    1. Level 2 - 1
    2. Level 2 - 2

No longer in list

', + ); }); test('Extreme multi-level lists with trailing paragraph', () { @@ -1001,44 +1050,44 @@ void main() { {'insert': 'Level 1 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 1 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 2 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Level 2 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Level 3 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'Level 3 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'Level 4 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, {'insert': 'Level 4 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, - {'insert': 'No longer in list\n'} + {'insert': 'No longer in list\n'}, ]); expect( codec.encode(doc), @@ -1069,37 +1118,39 @@ void main() { {'insert': 'Level 1 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 1 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Level 2 - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Level 2 - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'No longer in list\n'}, {'insert': 'In a new list - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'In a new list - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, ]); - expect(codec.encode(doc), - '

Test

  1. Level 1 - 1
  2. Level 1 - 2
    1. Level 2 - 1
    2. Level 2 - 2

No longer in list

  1. In a new list - 1
  2. In a new list - 2
'); + expect( + codec.encode(doc), + '

Test

  1. Level 1 - 1
  2. Level 1 - 2
    1. Level 2 - 1
    2. Level 2 - 2

No longer in list

  1. In a new list - 1
  2. In a new list - 2
', + ); }); test('Successive multi-level lists', () { @@ -1107,33 +1158,33 @@ void main() { {'insert': 'Unordered'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Sub - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Ordered - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Sub - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'Sub - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'Ordered - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} - } + 'attributes': {'block': 'ol'}, + }, ]); expect( codec.encode(doc), @@ -1159,38 +1210,38 @@ void main() { {'insert': 'Unordered'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Sub - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Sub - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': '\n'}, {'insert': 'Ordered - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Sub - 1'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Sub - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 1} + 'attributes': {'block': 'ol', 'indent': 1}, }, {'insert': 'Ordered - 2'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, ]); expect( @@ -1219,13 +1270,14 @@ void main() { {'insert': 'Something in the way...\nSomething in the way...'}, { 'insert': '\n', - 'attributes': {'indent': 1} + 'attributes': {'indent': 1}, }, ]); expect( - codec.encode(doc), - '

Something in the way...

' - '

Something in the way...

'); + codec.encode(doc), + '

Something in the way...

' + '

Something in the way...

', + ); }); test('Quotes with indent', () { @@ -1233,13 +1285,14 @@ void main() { {'insert': 'Something in the way...\nSomething in the way...'}, { 'insert': '\n', - 'attributes': {'block': 'quote', 'indent': 1} + 'attributes': {'block': 'quote', 'indent': 1}, }, ]); expect( - codec.encode(doc), - '

Something in the way...

' - '
Something in the way...
'); + codec.encode(doc), + '

Something in the way...

' + '
Something in the way...
', + ); }); test('Quote with embedded heading', () { @@ -1247,17 +1300,19 @@ void main() { {'insert': 'Quote'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, {'insert': 'header'}, { 'insert': '\n', - 'attributes': {'block': 'quote', 'heading': 1} + 'attributes': {'block': 'quote', 'heading': 1}, }, {'insert': 'Not in quote\n'}, ]); - expect(codec.encode(doc), - '
Quote

header

Not in quote

'); + expect( + codec.encode(doc), + '
Quote

header

Not in quote

', + ); }); }); @@ -1266,7 +1321,7 @@ void main() { final html = ''; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert(0, BlockEmbed.image('http://fake.link/image.png')); @@ -1277,12 +1332,15 @@ void main() { final html = ''; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert( - 0, - BlockEmbed.image('http://fake.link/image.png', - data: {'style': 'max-width: 100%; object-fit: cover;'})); + 0, + BlockEmbed.image( + 'http://fake.link/image.png', + data: {'style': 'max-width: 100%; object-fit: cover;'}, + ), + ); expect(codec.encode(doc), html); }); @@ -1290,7 +1348,7 @@ void main() { test('Line', () { final html = '
'; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert(0, BlockEmbed.horizontalRule); @@ -1309,7 +1367,7 @@ void main() { test('Plain paragraph', () { final html = 'Hello World!'; final doc = ParchmentDocument.fromJson([ - {'insert': 'Hello World!\n'} + {'insert': 'Hello World!\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1321,9 +1379,9 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello', - 'attributes': {'bg': 0xffff0000} + 'attributes': {'bg': 0xffff0000}, }, - {'insert': ' world!\n'} + {'insert': ' world!\n'}, ]); expect(codec.decode(htmlRGBA).toDelta(), doc.toDelta()); @@ -1335,9 +1393,9 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello', - 'attributes': {'fg': 0xffff0000} + 'attributes': {'fg': 0xffff0000}, }, - {'insert': ' world!\n'} + {'insert': ' world!\n'}, ]); expect(codec.decode(htmlRGBA).toDelta(), doc.toDelta()); @@ -1348,7 +1406,7 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': '\n'}, ]); @@ -1361,7 +1419,7 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'u': true} + 'attributes': {'u': true}, }, {'insert': '\n'}, ]); @@ -1374,7 +1432,7 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'s': true} + 'attributes': {'s': true}, }, {'insert': '\n'}, ]); @@ -1387,7 +1445,7 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, {'insert': '\n'}, ]); @@ -1400,7 +1458,7 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'i': true, 'b': true} + 'attributes': {'i': true, 'b': true}, }, {'insert': '\n'}, ]); @@ -1414,17 +1472,17 @@ void main() { {'insert': 'AAA'}, { 'insert': 'BB', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': 'B', - 'attributes': {'b': true, 's': true} + 'attributes': {'b': true, 's': true}, }, { 'insert': 'CCC', - 'attributes': {'s': true} + 'attributes': {'s': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1435,17 +1493,17 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Something ', - 'attributes': {'a': 'https://wikipedia.org', 'u': true} + 'attributes': {'a': 'https://wikipedia.org', 'u': true}, }, { 'insert': 'in the way', - 'attributes': {'i': true, 'u': true} + 'attributes': {'i': true, 'u': true}, }, { 'insert': ' mmmm...', - 'attributes': {'u': true} + 'attributes': {'u': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1458,7 +1516,7 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 1} + 'attributes': {'heading': 1}, }, ]); @@ -1471,8 +1529,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 2} - } + 'attributes': {'heading': 2}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1484,8 +1542,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 3} - } + 'attributes': {'heading': 3}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1497,8 +1555,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 4} - } + 'attributes': {'heading': 4}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1510,8 +1568,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 5} - } + 'attributes': {'heading': 5}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1523,8 +1581,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'heading': 6} - } + 'attributes': {'heading': 6}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1536,9 +1594,7 @@ void main() { test('simple', () { final html = '

Hello World!

'; final doc = ParchmentDocument.fromJson([ - { - 'insert': 'Hello World!\n', - } + {'insert': 'Hello World!\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1547,26 +1603,23 @@ void main() { final html = '

Hello World!

'; final doc = ParchmentHtmlCodec().decode(html); expect( - doc.toDelta(), - Delta() - ..insert('Hello World!') - ..insert('\n', {'indent': 1})); + doc.toDelta(), + Delta() + ..insert('Hello World!') + ..insert('\n', {'indent': 1}), + ); }); test('Paragraph with link', () { final html = '

Hello World!Hello World! Another hello world!

'; final doc = ParchmentDocument.fromJson([ - { - 'insert': 'Hello World!', - }, + {'insert': 'Hello World!'}, { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link'}, }, - { - 'insert': ' Another hello world!\n', - } + {'insert': ' Another hello world!\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1576,26 +1629,18 @@ void main() { final html = '

Hello World!Hello World! Another hello world!

Hello World!Hello World! Another hello world!

'; final doc = ParchmentDocument.fromJson([ - { - 'insert': 'Hello World!', - }, + {'insert': 'Hello World!'}, { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link'}, }, - { - 'insert': ' Another hello world!\n', - }, - { - 'insert': 'Hello World!', - }, + {'insert': ' Another hello world!\n'}, + {'insert': 'Hello World!'}, { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link'}, }, - { - 'insert': ' Another hello world!\n', - } + {'insert': ' Another hello world!\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1608,8 +1653,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'quote'} - } + 'attributes': {'block': 'quote'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1624,18 +1669,18 @@ void main() { {'insert': 'void main() {'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': ' print("Hello world!");'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': '}'}, { 'insert': '\n', - 'attributes': {'block': 'code'} - } + 'attributes': {'block': 'code'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1648,7 +1693,7 @@ void main() { {'insert': 'some code'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': 'Hello world\n'}, ]); @@ -1663,17 +1708,17 @@ void main() { {'insert': 'Hello world\n'}, { 'insert': 'Another', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, {'insert': ' one\n'}, {'insert': 'some '}, { 'insert': 'quote', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': '\n', - 'attributes': {'block': 'quote'} + 'attributes': {'block': 'quote'}, }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1685,13 +1730,13 @@ void main() { {'insert': 'an item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'another item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} - } + 'attributes': {'block': 'ol'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1701,12 +1746,12 @@ void main() { final doc = ParchmentDocument.fromJson([ { 'insert': 'Hello World!', - 'attributes': {'b': true} + 'attributes': {'b': true}, }, { 'insert': '\n', - 'attributes': {'block': 'ol'} - } + 'attributes': {'block': 'ol'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1718,13 +1763,13 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} - } + 'attributes': {'block': 'ul'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1738,8 +1783,8 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'center'} - } + 'attributes': {'alignment': 'center'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1753,18 +1798,18 @@ void main() { {'insert': 'Hello World!\nHello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'right'} + 'attributes': {'alignment': 'right'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'center'} + 'attributes': {'alignment': 'center'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'alignment': 'justify'} - } + 'attributes': {'alignment': 'justify'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1780,23 +1825,23 @@ void main() { {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'right'} + 'attributes': {'block': 'ol', 'alignment': 'right'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'center'} + 'attributes': {'block': 'ol', 'alignment': 'center'}, }, {'insert': 'Hello World!'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'alignment': 'justify'} - } + 'attributes': {'block': 'ol', 'alignment': 'justify'}, + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1832,62 +1877,62 @@ void main() { {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 2} + 'attributes': {'block': 'ol', 'indent': 2}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'item'}, { 'insert': '\n', - 'attributes': {'block': 'ol'} + 'attributes': {'block': 'ol'}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 2} + 'attributes': {'block': 'ul', 'indent': 2}, }, {'insert': 'sub-sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, {'insert': 'sub-sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ol', 'indent': 3} + 'attributes': {'block': 'ol', 'indent': 3}, }, {'insert': 'sub-sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 2} + 'attributes': {'block': 'ul', 'indent': 2}, }, {'insert': 'sub-item'}, { 'insert': '\n', - 'attributes': {'block': 'ul', 'indent': 1} + 'attributes': {'block': 'ul', 'indent': 1}, }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1899,7 +1944,7 @@ void main() { String html = '


' '

a

'; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert(0, 'a'); doc.insert(0, BlockEmbed.image('http://another.fake.link/image.png')); @@ -1911,7 +1956,7 @@ void main() { test('Image (no style)', () { final html = ''; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert(0, BlockEmbed.image('http://fake.link/image.png')); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1921,14 +1966,15 @@ void main() { final html = ''; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert( - 0, - BlockEmbed.image('http://fake.link/image.png', data: { - 'width': 100, - 'height': 100, - })); + 0, + BlockEmbed.image( + 'http://fake.link/image.png', + data: {'width': 100, 'height': 100}, + ), + ); expect(codec.decode(html).toDelta(), doc.toDelta()); }); @@ -1936,7 +1982,7 @@ void main() { test('Line', () { final html = '
'; final doc = ParchmentDocument.fromJson([ - {'insert': '\n'} + {'insert': '\n'}, ]); doc.insert(0, BlockEmbed.horizontalRule); @@ -1952,7 +1998,7 @@ void main() { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link'}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1966,7 +2012,7 @@ void main() { 'insert': 'Hello World!', 'attributes': {'a': 'http://fake.link', 'i': true}, }, - {'insert': '\n'} + {'insert': '\n'}, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1983,7 +2029,7 @@ void main() { { 'insert': '\n', 'attributes': {'block': 'ul'}, - } + }, ]); expect(codec.decode(html).toDelta(), doc.toDelta()); @@ -1996,86 +2042,86 @@ final doc = [ {'insert': 'Fleather'}, { 'insert': '\n', - 'attributes': {'heading': 1} + 'attributes': {'heading': 1}, }, { 'insert': 'Soft and gentle rich text editing for Flutter applications.', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, {'insert': '\nFleather is an '}, { 'insert': 'early preview', - 'attributes': {'b': true, 'fg': 0xFFFF0000} + 'attributes': {'b': true, 'fg': 0xFFFF0000}, }, {'insert': ' open source library.\nDocumentation'}, { 'insert': '\n', - 'attributes': {'heading': 3} + 'attributes': {'heading': 3}, }, {'insert': 'Quick Start'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Data format and Document Model'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Style attributes'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Heuristic rules'}, { 'insert': '\n', - 'attributes': {'block': 'ul'} + 'attributes': {'block': 'ul'}, }, {'insert': 'Clean and modern look'}, { 'insert': '\n', - 'attributes': {'heading': 2} + 'attributes': {'heading': 2}, }, {'insert': 'Fleather’s rich text editor is built with '}, { 'insert': 'simplicity and flexibility', - 'attributes': {'i': true} + 'attributes': {'i': true}, }, { 'insert': - ' in mind. It provides clean interface for distraction-free editing. Think ' + ' in mind. It provides clean interface for distraction-free editing. Think ', }, { 'insert': 'Medium.com', - 'attributes': {'c': true} + 'attributes': {'c': true}, }, // {'insert': '-like experience.\n'}, {'insert': '-like experience.\nimport ‘package:flutter/material.dart’;'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': 'import ‘package:parchment/parchment.dart’;'}, { 'insert': '\n\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': 'void main() {'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': ' print(“Hello world!”);'}, { 'insert': '\n', - 'attributes': {'block': 'code'} + 'attributes': {'block': 'code'}, }, {'insert': '}'}, { 'insert': '\n', - 'attributes': {'block': 'code'} - } + 'attributes': {'block': 'code'}, + }, ]; final delta = Delta.fromJson(doc); final htmlDoc = '

Fleather

' diff --git a/packages/parchment/test/codecs/markdown_test.dart b/packages/parchment/test/codecs/markdown_test.dart index 9af33088..9bb4b80c 100644 --- a/packages/parchment/test/codecs/markdown_test.dart +++ b/packages/parchment/test/codecs/markdown_test.dart @@ -75,11 +75,13 @@ void main() { } runFor( - 'Okay, _this is in italics_ and _so is all of _ this_ but this is not\n\n', - true); + 'Okay, _this is in italics_ and _so is all of _ this_ but this is not\n\n', + true, + ); runFor( - 'Okay, *this is in italics* and *so is all of _ this* but this is not\n\n', - false); + 'Okay, *this is in italics* and *so is all of _ this* but this is not\n\n', + false, + ); }); test('bold', () { @@ -122,11 +124,13 @@ void main() { } runFor( - 'Okay, **this is bold** and **so is all of __ this** but this is not\n\n', - true); + 'Okay, **this is bold** and **so is all of __ this** but this is not\n\n', + true, + ); runFor( - 'Okay, __this is bold__ and __so is all of __ this__ but this is not\n\n', - false); + 'Okay, __this is bold__ and __so is all of __ this__ but this is not\n\n', + false, + ); }); test('strike through', () { @@ -200,11 +204,14 @@ void main() { } runFor( - '**this is bold** _this is in italics_ and **_this is both_**\n\n'); + '**this is bold** _this is in italics_ and **_this is both_**\n\n', + ); runFor( - '**this is bold** *this is in italics* and ***this is both***\n\n'); + '**this is bold** *this is in italics* and ***this is both***\n\n', + ); runFor( - '__this is bold__ _this is in italics_ and ___this is both___\n\n'); + '__this is bold__ _this is in italics_ and ___this is both___\n\n', + ); }); test('link', () { @@ -328,11 +335,14 @@ void main() { final delta = document.toDelta(); expect( - delta, - Delta() - ..insert('This is an H$level') - ..insert( - '\n', ParchmentAttribute.heading.withValue(level).toJson())); + delta, + Delta() + ..insert('This is an H$level') + ..insert( + '\n', + ParchmentAttribute.heading.withValue(level).toJson(), + ), + ); final andBack = parchmentMarkdown.encode(document); expect(andBack, markdown); @@ -479,8 +489,9 @@ void main() { group('ParchmentMarkdownCodec.encode', () { test('split adjacent paragraphs', () { final delta = Delta()..insert('First line\nSecond line\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, 'First line\n\nSecond line\n\n'); }); @@ -493,15 +504,18 @@ void main() { ..insert('circus', attribute.toJson()) ..insert('\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, expected); } runFor(ParchmentAttribute.bold, 'This **house** is a **circus**\n\n'); runFor(ParchmentAttribute.italic, 'This _house_ is a _circus_\n\n'); - runFor(ParchmentAttribute.strikethrough, - 'This ~~house~~ is a ~~circus~~\n\n'); + runFor( + ParchmentAttribute.strikethrough, + 'This ~~house~~ is a ~~circus~~\n\n', + ); }); test('intersecting inline styles', () { @@ -517,8 +531,9 @@ void main() { ..insert('circus', b) ..insert('\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, 'This **house _is a_ circus**\n\n'); }); @@ -532,8 +547,9 @@ void main() { ..insert(' circus ', i) ..insert('\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, 'This **house** is a _circus_ \n\n'); }); @@ -547,8 +563,9 @@ void main() { ..insert(' circus ', i) ..insert('\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, 'This **_house_** is a _circus_ \n\n'); }); @@ -562,19 +579,24 @@ void main() { ..insert(' circus ', link.toJson()) ..insert('\n'); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, 'This **house** is a [circus](https://github.com) \n\n'); }); test('heading styles', () { void runFor( - ParchmentAttribute attribute, String source, String expected) { + ParchmentAttribute attribute, + String source, + String expected, + ) { final delta = Delta() ..insert(source) ..insert('\n', attribute.toJson()); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, expected); } @@ -587,13 +609,17 @@ void main() { }); test('block styles', () { - void runFor(ParchmentAttribute attribute, String source, - String expected) { + void runFor( + ParchmentAttribute attribute, + String source, + String expected, + ) { final delta = Delta() ..insert(source) ..insert('\n', attribute.toJson()); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, expected); } @@ -607,16 +633,20 @@ void main() { final delta = Delta() ..insert('Hello') ..insert('\n', ParchmentAttribute.ul.toJson()); - expect(parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)), - '* Hello\n\n'); expect( - ParchmentMarkdownCodec(unorderedListToken: '-') - .encode(ParchmentDocument.fromDelta(delta)), - '- Hello\n\n'); + parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)), + '* Hello\n\n', + ); + expect( + ParchmentMarkdownCodec(unorderedListToken: '-') + .encode(ParchmentDocument.fromDelta(delta)), + '- Hello\n\n', + ); expect( - ParchmentMarkdownCodec(unorderedListToken: '+') - .encode(ParchmentDocument.fromDelta(delta)), - '+ Hello\n\n'); + ParchmentMarkdownCodec(unorderedListToken: '+') + .encode(ParchmentDocument.fromDelta(delta)), + '+ Hello\n\n', + ); }); test('ol', () { @@ -627,8 +657,9 @@ void main() { ..insert('\n', ParchmentAttribute.ol.toJson()) ..insert('List') ..insert('\n', ParchmentAttribute.ol.toJson()); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); final expected = '1. Hello\n2. This is a\n3. List\n\n'; expect(result, expected); }); @@ -641,8 +672,9 @@ void main() { ..insert('\n', {'block': 'ol', 'indent': 1}) ..insert('List') ..insert('\n', {'block': 'ol'}); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); final expected = '1. Hello\n 1. This is a\n2. List\n\n'; expect(result, expected); }); @@ -651,31 +683,34 @@ void main() { final delta = Delta() ..insert('Hello') ..insert('\n', ParchmentAttribute.cl.toJson()) - ..insert( - 'This is a', - ) + ..insert('This is a') ..insert('\n', { ...ParchmentAttribute.cl.toJson(), ...ParchmentAttribute.checked.toJson(), }) ..insert('Checklist') ..insert('\n', ParchmentAttribute.cl.toJson()); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); final expected = '- [ ] Hello\n- [X] This is a\n- [ ] Checklist\n\n'; expect(result, expected); }); test('multiline blocks', () { - void runFor(ParchmentAttribute attribute, String source, - String expected) { + void runFor( + ParchmentAttribute attribute, + String source, + String expected, + ) { final delta = Delta() ..insert(source) ..insert('\n', attribute.toJson()) ..insert(source) ..insert('\n', attribute.toJson()); - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, expected); } @@ -686,8 +721,9 @@ void main() { }); test('multiple styles', () { - final result = - parchmentMarkdown.encode(ParchmentDocument.fromDelta(delta)); + final result = parchmentMarkdown.encode( + ParchmentDocument.fromDelta(delta), + ); expect(result, markdown); }); }); diff --git a/packages/parchment/test/document/attributes_test.dart b/packages/parchment/test/document/attributes_test.dart index b5d1df1b..5aa99445 100644 --- a/packages/parchment/test/document/attributes_test.dart +++ b/packages/parchment/test/document/attributes_test.dart @@ -18,30 +18,34 @@ void main() { group('$ParchmentAttribute', () { test('create background attribute with color', () { - final attribute = - ParchmentAttribute.backgroundColor.withColor(0xFFFF0000); + final attribute = ParchmentAttribute.backgroundColor.withColor( + 0xFFFF0000, + ); expect(attribute.key, ParchmentAttribute.backgroundColor.key); expect(attribute.scope, ParchmentAttribute.backgroundColor.scope); expect(attribute.value, 0xFFFF0000); }); test('create background attribute with transparent color', () { - final attribute = - ParchmentAttribute.backgroundColor.withColor(0x00000000); + final attribute = ParchmentAttribute.backgroundColor.withColor( + 0x00000000, + ); expect(attribute, ParchmentAttribute.backgroundColor.unset); }); test('create foreground attribute with color', () { - final attribute = - ParchmentAttribute.foregroundColor.withColor(0x00FF0000); + final attribute = ParchmentAttribute.foregroundColor.withColor( + 0x00FF0000, + ); expect(attribute.key, ParchmentAttribute.foregroundColor.key); expect(attribute.scope, ParchmentAttribute.foregroundColor.scope); expect(attribute.value, 0x00FF0000); }); test('create foreground attribute with black color', () { - final attribute = - ParchmentAttribute.foregroundColor.withColor(0x00000000); + final attribute = ParchmentAttribute.foregroundColor.withColor( + 0x00000000, + ); expect(attribute, ParchmentAttribute.foregroundColor.unset); }); }); diff --git a/packages/parchment/test/document/block_test.dart b/packages/parchment/test/document/block_test.dart index 2428d238..e1e7a624 100644 --- a/packages/parchment/test/document/block_test.dart +++ b/packages/parchment/test/document/block_test.dart @@ -84,8 +84,10 @@ void main() { expect(block.childCount, 1); expect(block.first, const TypeMatcher()); final line = block.first as LineNode; - expect(line.style.get(ParchmentAttribute.alignment), - ParchmentAttribute.right); + expect( + line.style.get(ParchmentAttribute.alignment), + ParchmentAttribute.right, + ); }); test('format two sibling lines as list', () { @@ -103,7 +105,10 @@ void main() { test('format to split first line from block', () { root.insert( - 0, 'London Grammar Songs\nHey now\nStrong\nIf You Wait', null); + 0, + 'London Grammar Songs\nHey now\nStrong\nIf You Wait', + null, + ); root.retain(20, 1, h1Attrs); root.retain(28, 1, ulAttrs); root.retain(35, 1, ulAttrs); @@ -125,7 +130,10 @@ void main() { test('format to split last line from block', () { root.insert( - 0, 'London Grammar Songs\nHey now\nStrong\nIf You Wait', null); + 0, + 'London Grammar Songs\nHey now\nStrong\nIf You Wait', + null, + ); root.retain(20, 1, h1Attrs); root.retain(28, 1, ulAttrs); root.retain(35, 1, ulAttrs); @@ -147,7 +155,10 @@ void main() { test('format to split middle line from block', () { root.insert( - 0, 'London Grammar Songs\nHey now\nStrong\nIf You Wait', null); + 0, + 'London Grammar Songs\nHey now\nStrong\nIf You Wait', + null, + ); root.retain(20, 1, h1Attrs); root.retain(28, 1, ulAttrs); root.retain(35, 1, ulAttrs); @@ -169,7 +180,10 @@ void main() { test('insert line-break at the beginning of the document', () { root.insert( - 0, 'London Grammar Songs\nHey now\nStrong\nIf You Wait', null); + 0, + 'London Grammar Songs\nHey now\nStrong\nIf You Wait', + null, + ); root.retain(20, 1, ulAttrs); root.retain(28, 1, ulAttrs); root.retain(35, 1, ulAttrs); diff --git a/packages/parchment/test/document/leaf_test.dart b/packages/parchment/test/document/leaf_test.dart index 78406c96..37a59611 100644 --- a/packages/parchment/test/document/leaf_test.dart +++ b/packages/parchment/test/document/leaf_test.dart @@ -101,18 +101,9 @@ void main() { expect(line.childCount, 4); expect(line.children.last.offset, 9); final b = boldStyle.toJson(); - expect( - line.children.elementAt(0).toDelta(), - Delta()..insert('Lon', b), - ); - expect( - line.children.elementAt(1).toDelta(), - Delta()..insert('don'), - ); - expect( - line.children.elementAt(2).toDelta(), - Delta()..insert('don', b), - ); + expect(line.children.elementAt(0).toDelta(), Delta()..insert('Lon', b)); + expect(line.children.elementAt(1).toDelta(), Delta()..insert('don')); + expect(line.children.elementAt(2).toDelta(), Delta()..insert('don', b)); }); }); @@ -164,8 +155,10 @@ void main() { }); test('toDelta', () { - expect(node.toDelta(), - Delta()..insert(EmbeddableObject('hr', inline: false).toJson())); + expect( + node.toDelta(), + Delta()..insert(EmbeddableObject('hr', inline: false).toJson()), + ); }); test('splitAt', () { @@ -197,8 +190,10 @@ void main() { final text = LeafNode('Text'); final style = ParchmentStyle().put(ParchmentAttribute.block.numberList); - expect(() => text.applyStyle(style), - throwsA(const TypeMatcher())); + expect( + () => text.applyStyle(style), + throwsA(const TypeMatcher()), + ); }); }); } diff --git a/packages/parchment/test/document/line_test.dart b/packages/parchment/test/document/line_test.dart index c098e1ba..4bc4474f 100644 --- a/packages/parchment/test/document/line_test.dart +++ b/packages/parchment/test/document/line_test.dart @@ -41,7 +41,10 @@ void main() { test('nextLine', () { root.insert( - 0, 'Hello world\nThis is my first multiline\nItem\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\nItem\ndocument.', + null, + ); root.retain(38, 1, ulStyle); root.retain(43, 1, bqStyle); final line = root.first as LineNode; @@ -240,7 +243,10 @@ void main() { test('delete empty line', () { root.insert( - 0, 'Hello world\nThis is my first multiline\n\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\n\ndocument.', + null, + ); expect(root.childCount, 4); root.delete(39, 1); expect(root.childCount, 3); @@ -248,7 +254,10 @@ void main() { test('delete line-break of non-empty line', () { root.insert( - 0, 'Hello world\nThis is my first multiline\n\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\n\ndocument.', + null, + ); root.retain(39, 1, h2Style); expect(root.childCount, 4); root.delete(38, 1); @@ -259,7 +268,10 @@ void main() { test('insert at the beginning of a line', () { root.insert( - 0, 'Hello world\nThis is my first multiline\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\ndocument.', + null, + ); root.insert(12, 'Boom! ', null); expect(root.childCount, 3); expect(root.children.elementAt(1), hasLength(33)); @@ -267,7 +279,10 @@ void main() { test('delete last character of a line', () { root.insert( - 0, 'Hello world\nThis is my first multiline\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\ndocument.', + null, + ); root.delete(37, 1); expect(root.childCount, 3); final line = root.children.elementAt(1) as LineNode; @@ -277,7 +292,10 @@ void main() { test('collectStyle', () { // TODO: need more test cases for collectStyle root.insert( - 0, 'Hello world\nThis is my first multiline\n\ndocument.', null); + 0, + 'Hello world\nThis is my first multiline\n\ndocument.', + null, + ); root.retain(38, 1, h2Style); root.retain(23, 5, boldStyle); var result = root.lookup(20); diff --git a/packages/parchment/test/document_test.dart b/packages/parchment/test/document_test.dart index c2adfa60..20f9b31d 100644 --- a/packages/parchment/test/document_test.dart +++ b/packages/parchment/test/document_test.dart @@ -58,22 +58,26 @@ void main() { test('load non-empty document', () { final doc = dartconfEmbedDoc(); - expect(doc.toPlainText(), - 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n'); + expect( + doc.toPlainText(), + 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n', + ); }); test('load document with 0.x version embeds', () { final delta = Delta() ..insert('DartConf\n') ..insert('\u200B', { - 'embed': {'type': 'hr'} + 'embed': {'type': 'hr'}, }) ..insert('\n') ..insert('Los Angeles\n'); final doc = ParchmentDocument.fromJson(jsonDecode(jsonEncode(delta))); - expect(doc.toPlainText(), - 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n'); + expect( + doc.toPlainText(), + 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n', + ); final line = doc.root.children.toList()[1] as LineNode; final node = line.children.single as LeafNode; expect(node, isA()); @@ -239,8 +243,10 @@ void main() { // an embed. final doc = dartconfEmbedDoc(); doc.delete(8, 1); - expect(doc.toPlainText(), - 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n'); + expect( + doc.toPlainText(), + 'DartConf\n${EmbedNode.kObjectReplacementCharacter}\nLos Angeles\n', + ); }); test('delete preserves last newline character', () { @@ -253,8 +259,10 @@ void main() { var delta = Delta() ..insert('Line 1\nLine 2\n') ..insert('\n', {'block': 'ul'}); - final document = ParchmentDocument.fromDelta(delta, - heuristics: ParchmentHeuristics.fallback); + final document = ParchmentDocument.fromDelta( + delta, + heuristics: ParchmentHeuristics.fallback, + ); var blockLine = (document.root.children.first.next!.next! as BlockNode).first; // Set _offsetCache of LineNode in BlockNode to 0 @@ -325,8 +333,10 @@ void main() { doc.insert(4, BlockEmbed.horizontalRule); expect(doc.root.children, hasLength(4)); expect(doc.root.children.elementAt(0).toPlainText(), 'Dart\n'); - expect(doc.root.children.elementAt(1).toPlainText(), - '${EmbedNode.kObjectReplacementCharacter}\n'); + expect( + doc.root.children.elementAt(1).toPlainText(), + '${EmbedNode.kObjectReplacementCharacter}\n', + ); expect(doc.root.children.elementAt(2).toPlainText(), 'Conf\n'); expect(doc.root.children.elementAt(3).toPlainText(), 'Los Angeles\n'); final line = doc.root.children.elementAt(1) as LineNode; @@ -358,8 +368,10 @@ void main() { doc.insert(9, 'text'); expect(doc.root.children, hasLength(4)); expect(doc.root.children.elementAt(1).toPlainText(), 'text\n'); - expect(doc.root.children.elementAt(2).toPlainText(), - '${EmbedNode.kObjectReplacementCharacter}\n'); + expect( + doc.root.children.elementAt(2).toPlainText(), + '${EmbedNode.kObjectReplacementCharacter}\n', + ); }); test('insert text after embed', () { @@ -368,8 +380,10 @@ void main() { expect(doc.root.children, hasLength(3)); doc.insert(10, 'text'); expect(doc.root.children, hasLength(4)); - expect(doc.root.children.elementAt(1).toPlainText(), - '${EmbedNode.kObjectReplacementCharacter}\n'); + expect( + doc.root.children.elementAt(1).toPlainText(), + '${EmbedNode.kObjectReplacementCharacter}\n', + ); expect(doc.root.children.elementAt(2).toPlainText(), 'text\n'); }); @@ -378,8 +392,10 @@ void main() { doc.replace(4, 4, BlockEmbed.horizontalRule); expect(doc.root.children, hasLength(3)); expect(doc.root.children.elementAt(0).toPlainText(), 'Dart\n'); - expect(doc.root.children.elementAt(1).toPlainText(), - '${EmbedNode.kObjectReplacementCharacter}\n'); + expect( + doc.root.children.elementAt(1).toPlainText(), + '${EmbedNode.kObjectReplacementCharacter}\n', + ); expect(doc.root.children.elementAt(2).toPlainText(), 'Los Angeles\n'); }); @@ -405,8 +421,10 @@ void main() { expect(doc.root.children, hasLength(4)); expect(doc.root.children.elementAt(0).toPlainText(), 'Dart\n'); expect(doc.root.children.elementAt(1).toPlainText(), '\n'); - expect(doc.root.children.elementAt(2).toPlainText(), - '${EmbedNode.kObjectReplacementCharacter}\n'); + expect( + doc.root.children.elementAt(2).toPlainText(), + '${EmbedNode.kObjectReplacementCharacter}\n', + ); expect(doc.root.children.elementAt(3).toPlainText(), 'Los Angeles\n'); }); }); diff --git a/packages/parchment/test/heuristics/format_rules_test.dart b/packages/parchment/test/heuristics/format_rules_test.dart index 6fe3a4a9..53a7e96f 100644 --- a/packages/parchment/test/heuristics/format_rules_test.dart +++ b/packages/parchment/test/heuristics/format_rules_test.dart @@ -97,10 +97,12 @@ void main() { final rule = FormatLinkAtCaretPositionRule(); test('apply', () { - final link = ParchmentAttribute.link - .fromString('https://github.com/fleather-editor/bold'); - final newLink = ParchmentAttribute.link - .fromString('https://github.com/fleather-editor/fleather'); + final link = ParchmentAttribute.link.fromString( + 'https://github.com/fleather-editor/bold', + ); + final newLink = ParchmentAttribute.link.fromString( + 'https://github.com/fleather-editor/fleather', + ); final doc = Delta() ..insert('Visit our ') ..insert('website', link.toJson()) diff --git a/packages/parchment/test/heuristics/insert_rules_test.dart b/packages/parchment/test/heuristics/insert_rules_test.dart index 100d0a4c..c1ed995d 100644 --- a/packages/parchment/test/heuristics/insert_rules_test.dart +++ b/packages/parchment/test/heuristics/insert_rules_test.dart @@ -123,21 +123,23 @@ void main() { group('$AutoExitBlockRule', () { final rule = AutoExitBlockRule(); - test('applies when newline is inserted on the last empty line in a block', - () { - final ul = ParchmentAttribute.ul.toJson(); - final doc = Delta() - ..insert('Item 1') - ..insert('\n', ul) - ..insert('Item 2') - ..insert('\n\n', ul); - final actual = rule.apply(doc, 14, '\n'); - expect(actual, isNotNull); - final expected = Delta() - ..retain(14) - ..retain(1, ParchmentAttribute.block.unset.toJson()); - expect(actual, expected); - }); + test( + 'applies when newline is inserted on the last empty line in a block', + () { + final ul = ParchmentAttribute.ul.toJson(); + final doc = Delta() + ..insert('Item 1') + ..insert('\n', ul) + ..insert('Item 2') + ..insert('\n\n', ul); + final actual = rule.apply(doc, 14, '\n'); + expect(actual, isNotNull); + final expected = Delta() + ..retain(14) + ..retain(1, ParchmentAttribute.block.unset.toJson()); + expect(actual, expected); + }, + ); test('applies only on empty line', () { final ul = ParchmentAttribute.ul.toJson(); @@ -208,8 +210,9 @@ void main() { test('apply preserve link formatting within link', () { final doc = Delta() ..insert('Doc with link') - ..insert('http://fleather-editor.github.io', - {'a': 'http://fleather-editor.github.io'}) + ..insert('http://fleather-editor.github.io', { + 'a': 'http://fleather-editor.github.io', + }) ..insert(' link'); final actual = rule.apply(doc, 17, 's'); final expected = Delta() @@ -221,8 +224,9 @@ void main() { test('apply remove link formatting on link boundaries', () { final doc = Delta() ..insert('Doc with link') - ..insert('http://fleather-editor.github.io', - {'a': 'http://fleather-editor.github.io'}) + ..insert('http://fleather-editor.github.io', { + 'a': 'http://fleather-editor.github.io', + }) ..insert(' link'); final actual = rule.apply(doc, 13, 'like this '); final expected = Delta()