From 5e9c804372d48d7d5d4d8a91898d625a7f666d9d Mon Sep 17 00:00:00 2001 From: Brett Morgan Date: Fri, 4 Jun 2021 13:51:00 +1000 Subject: [PATCH 1/3] Adding flutter_lints --- super_editor/analysis_options.yaml | 6 +- super_editor/example/analysis_options.yaml | 6 +- .../lib/demos/demo_attributed_text.dart | 47 +- .../lib/demos/demo_document_loses_focus.dart | 4 +- .../demos/demo_markdown_serialization.dart | 10 +- .../lib/demos/demo_selectable_text.dart | 49 +- .../demos/demo_switch_document_content.dart | 6 +- .../lib/demos/example_editor/_toolbar.dart | 122 ++- .../lib/demos/sliver_example_editor.dart | 8 +- .../demos/supertextfield/_emojis_demo.dart | 23 +- .../_expanding_multi_line_demo.dart | 17 +- .../supertextfield/_interactive_demo.dart | 51 +- .../lib/demos/supertextfield/_robot.dart | 34 +- .../supertextfield/_single_line_demo.dart | 25 +- .../_static_multi_line_demo.dart | 17 +- .../_textfield_demo_screen.dart | 8 +- .../demos/supertextfield/demo_textfield.dart | 4 +- super_editor/example/lib/main.dart | 27 +- .../marketing_video/main_marketing_video.dart | 25 +- super_editor/example/pubspec.lock | 14 + .../attributed_spans_test.dart | 212 ++++- .../attributed_text_test.dart | 66 +- .../lib/src/core/document_editor.dart | 16 +- .../lib/src/default_editor/blockquote.dart | 42 +- .../lib/src/default_editor/box_component.dart | 63 +- .../common_editor_operations.dart | 353 ++++--- .../default_editor/document_interaction.dart | 123 ++- .../document_keyboard_actions.dart | 109 ++- .../src/default_editor/horizontal_rule.dart | 41 +- .../lib/src/default_editor/image.dart | 45 +- .../lib/src/default_editor/list_items.dart | 103 +- .../lib/src/default_editor/styles.dart | 4 +- .../lib/src/default_editor/super_editor.dart | 15 +- super_editor/lib/src/default_editor/text.dart | 247 +++-- .../lib/src/default_editor/text_tools.dart | 13 +- .../src/default_editor/unknown_component.dart | 4 +- .../_platform_detector_web.dart | 1 + .../src/infrastructure/attributed_spans.dart | 205 ++-- .../infrastructure/super_selectable_text.dart | 114 ++- .../src/infrastructure/super_textfield.dart | 312 +++++-- .../lib/src/infrastructure/text_layout.dart | 22 +- .../lib/src/serialization/markdown.dart | 25 +- super_editor/pubspec.lock | 18 +- super_editor/pubspec.yaml | 2 +- .../editor_entry_smoke_test.dart | 12 +- .../test/serialization/markdown_test.dart | 180 +++- .../test/src/_document_test_tools.dart | 2 +- .../src/default_editor/attributions_test.dart | 16 +- .../document_keyboard_actions_test.dart | 39 +- .../test/src/default_editor/text_test.dart | 37 +- .../infrastructure/attributed_spans_test.dart | 248 +++-- .../infrastructure/attributed_text_test.dart | 105 ++- .../infrastructure/super_textfield_test.dart | 882 +++++++++++------- 53 files changed, 2752 insertions(+), 1427 deletions(-) diff --git a/super_editor/analysis_options.yaml b/super_editor/analysis_options.yaml index 81dbfcdf95..61140b2eb5 100644 --- a/super_editor/analysis_options.yaml +++ b/super_editor/analysis_options.yaml @@ -1,8 +1,10 @@ -include: package:pedantic/analysis_options.1.11.0.yaml +include: package:flutter_lints/flutter.yaml analyzer: exclude: [build/**] linter: rules: - omit_local_variable_types: false \ No newline at end of file + avoid_print: false + omit_local_variable_types: false + use_key_in_widget_constructors: false diff --git a/super_editor/example/analysis_options.yaml b/super_editor/example/analysis_options.yaml index 43773a5aeb..66bdb93887 100644 --- a/super_editor/example/analysis_options.yaml +++ b/super_editor/example/analysis_options.yaml @@ -1,8 +1,10 @@ -include: package:pedantic/analysis_options.yaml +include: package:flutter_lints/flutter.yaml analyzer: exclude: [lib/spikes] linter: rules: - omit_local_variable_types: false \ No newline at end of file + avoid_print: false + omit_local_variable_types: false + use_key_in_widget_constructors: false diff --git a/super_editor/example/lib/demos/demo_attributed_text.dart b/super_editor/example/lib/demos/demo_attributed_text.dart index 79c0e35d50..a0d15ae8af 100644 --- a/super_editor/example/lib/demos/demo_attributed_text.dart +++ b/super_editor/example/lib/demos/demo_attributed_text.dart @@ -68,19 +68,19 @@ class _AttributedTextDemoState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 20.0), + const Padding( + padding: EdgeInsets.only(left: 20.0), child: Text( 'AttributedText', style: TextStyle( - color: const Color(0xFF888888), + color: Color(0xFF888888), fontSize: 32, ), ), ), - SizedBox(height: 4), - Padding( - padding: const EdgeInsets.only(left: 20.0), + const SizedBox(height: 4), + const Padding( + padding: EdgeInsets.only(left: 20.0), child: Text( '''AttributedText is a data structure that supports an arbitrary number of "attribution spans". These attributions can be anything, and mean anything. @@ -90,21 +90,21 @@ That TextSpan can then be rendered by Flutter, as usual. Try it yourself by adding and removing attributions to characters in a string...''', style: TextStyle( - color: const Color(0xFF888888), + color: Color(0xFF888888), fontSize: 14, height: 1.4, fontWeight: FontWeight.normal, ), ), ), - SizedBox(height: 24), - SizedBox( + const SizedBox(height: 24), + const SizedBox( width: 600, child: Divider(), ), - SizedBox(height: 24), + const SizedBox(height: 24), Table( - defaultColumnWidth: IntrinsicColumnWidth(), + defaultColumnWidth: const IntrinsicColumnWidth(), defaultVerticalAlignment: TableCellVerticalAlignment.middle, children: [ TableRow( @@ -125,7 +125,7 @@ Try it yourself by adding and removing attributions to characters in a string... _buildCellSelector(_strikethroughRanges), ], ), - TableRow( + const TableRow( children: [ SizedBox(height: 24), SizedBox(height: 24), @@ -136,7 +136,7 @@ Try it yourself by adding and removing attributions to characters in a string... _buildRowTitle('Attributed Text'), SuperSelectableText( key: GlobalKey(), - textSpan: _richText ?? TextSpan(text: 'error'), + textSpan: _richText ?? const TextSpan(text: 'error'), ), ], ), @@ -202,7 +202,8 @@ class _TextRangeSelectorState extends State { } void _onTapUp(TapUpDetails details) { - final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = + _getCellIndexFromLocalOffset(details.localPosition); setState(() { _selectedCells[selectedCellIndex] = !_selectedCells[selectedCellIndex]; _reportSelectedRanges(); @@ -210,12 +211,14 @@ class _TextRangeSelectorState extends State { } void _onPanStart(DragStartDetails details) { - final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = + _getCellIndexFromLocalOffset(details.localPosition); _selectionMode = _selectedCells[selectedCellIndex] ? 'deselect' : 'select'; } void _onPanUpdate(DragUpdateDetails details) { - final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = + _getCellIndexFromLocalOffset(details.localPosition); setState(() { _selectedCells[selectedCellIndex] = _selectionMode == 'select'; _reportSelectedRanges(); @@ -223,7 +226,9 @@ class _TextRangeSelectorState extends State { } int _getCellIndexFromLocalOffset(Offset localOffset) { - return ((localOffset.dx / widget.cellWidth).floor()).clamp(0.0, widget.cellCount - 1).toInt(); + return ((localOffset.dx / widget.cellWidth).floor()) + .clamp(0.0, widget.cellCount - 1) + .toInt(); } void _reportSelectedRanges() { @@ -264,8 +269,12 @@ class _TextRangeSelectorState extends State { width: widget.cellWidth, height: widget.cellHeight, decoration: BoxDecoration( - border: Border.all(width: 1, color: _isSelected(index) ? Colors.red : Colors.grey), - color: _isSelected(index) ? Colors.red.withOpacity(0.7) : Colors.grey.withOpacity(0.7), + border: Border.all( + width: 1, + color: _isSelected(index) ? Colors.red : Colors.grey), + color: _isSelected(index) + ? Colors.red.withOpacity(0.7) + : Colors.grey.withOpacity(0.7), ), ), ), diff --git a/super_editor/example/lib/demos/demo_document_loses_focus.dart b/super_editor/example/lib/demos/demo_document_loses_focus.dart index e57f0974b7..133acfa816 100644 --- a/super_editor/example/lib/demos/demo_document_loses_focus.dart +++ b/super_editor/example/lib/demos/demo_document_loses_focus.dart @@ -46,8 +46,8 @@ class _LoseFocusDemoState extends State { } Widget _buildDocSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 48.0), + return const Padding( + padding: EdgeInsets.symmetric(horizontal: 48.0), child: TextField( decoration: InputDecoration( hintText: 'tap to give focus to this TextField', diff --git a/super_editor/example/lib/demos/demo_markdown_serialization.dart b/super_editor/example/lib/demos/demo_markdown_serialization.dart index 789c99f3a5..e99ac045fd 100644 --- a/super_editor/example/lib/demos/demo_markdown_serialization.dart +++ b/super_editor/example/lib/demos/demo_markdown_serialization.dart @@ -11,7 +11,8 @@ import 'package:super_editor/super_editor.dart'; /// current structure of the document in the editor. class MarkdownSerializationDemo extends StatefulWidget { @override - _MarkdownSerializationDemoState createState() => _MarkdownSerializationDemoState(); + _MarkdownSerializationDemoState createState() => + _MarkdownSerializationDemoState(); } class _MarkdownSerializationDemoState extends State { @@ -75,11 +76,12 @@ class _MarkdownSerializationDemoState extends State { color: const Color(0xFF222222), child: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), + padding: + const EdgeInsets.symmetric(horizontal: 24, vertical: 24), child: Text( _markdown, - style: TextStyle( - color: const Color(0xFFEEEEEE), + style: const TextStyle( + color: Color(0xFFEEEEEE), height: 1.4, ), ), diff --git a/super_editor/example/lib/demos/demo_selectable_text.dart b/super_editor/example/lib/demos/demo_selectable_text.dart index a3fa59885d..4268b5ced4 100644 --- a/super_editor/example/lib/demos/demo_selectable_text.dart +++ b/super_editor/example/lib/demos/demo_selectable_text.dart @@ -8,10 +8,10 @@ class SelectableTextDemo extends StatefulWidget { } class _SelectableTextDemoState extends State { - final _demoText1 = TextSpan( + final _demoText1 = const TextSpan( text: 'Super Editor', style: TextStyle( - color: const Color(0xFF444444), + color: Color(0xFF444444), fontSize: 18, height: 1.4, fontWeight: FontWeight.bold, @@ -20,7 +20,7 @@ class _SelectableTextDemoState extends State { TextSpan( text: ' is an open source text editor for Flutter projects.', style: TextStyle( - color: const Color(0xFF444444), + color: Color(0xFF444444), fontSize: 18, height: 1.4, fontWeight: FontWeight.normal, @@ -43,46 +43,48 @@ class _SelectableTextDemoState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildTitle('SuperSelectableText Widget'), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( title: 'EMPTY TEXT WITH CARET', demo: SuperSelectableText.plain( text: '', - textSelection: TextSelection.collapsed(offset: 0), + textSelection: const TextSelection.collapsed(offset: 0), showCaret: true, - style: TextStyle( - color: const Color(0xFF444444), + style: const TextStyle( + color: Color(0xFF444444), fontSize: 18, height: 1.4, ), ), ), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( title: 'TEXT WITHOUT SELECTION OR CARET', demo: SuperSelectableText( textSpan: _demoText1, ), ), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( title: 'TEXT WITH CARET + COLLAPSED SELECTION', demo: SuperSelectableText( textSpan: _demoText1, - textSelection: TextSelection.collapsed(offset: _demoText1.toPlainText().length), + textSelection: TextSelection.collapsed( + offset: _demoText1.toPlainText().length), showCaret: true, ), ), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( title: 'TEXT WITH LEFT-TO-RIGHT SELECTION + CARET', demo: SuperSelectableText( textSpan: _demoText1, - textSelection: const TextSelection(baseOffset: 0, extentOffset: 12), + textSelection: + const TextSelection(baseOffset: 0, extentOffset: 12), showCaret: true, ), ), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( title: 'TEXT WITH RIGHT-TO-LEFT SELECTION + CARET', demo: SuperSelectableText( @@ -93,9 +95,10 @@ class _SelectableTextDemoState extends State { showCaret: true, ), ), - SizedBox(height: 24), + const SizedBox(height: 24), _buildDemo( - title: 'TEXT WITH FULL SELECTION + CARET, CUSTOM COLORS, CARET SHAPE, DEBUG PAINT', + title: + 'TEXT WITH FULL SELECTION + CARET, CUSTOM COLORS, CARET SHAPE, DEBUG PAINT', demo: DebugSelectableTextDecorator( selectableTextKey: _debugTextKey, textLength: _demoText1.toPlainText().length, @@ -103,8 +106,10 @@ class _SelectableTextDemoState extends State { child: SuperSelectableText( key: _debugTextKey, textSpan: _demoText1, - textSelection: TextSelection(baseOffset: 0, extentOffset: _demoText1.toPlainText().length), - textSelectionDecoration: TextSelectionDecoration( + textSelection: TextSelection( + baseOffset: 0, + extentOffset: _demoText1.toPlainText().length), + textSelectionDecoration: const TextSelectionDecoration( selectionColor: Colors.yellow, ), showCaret: true, @@ -127,8 +132,8 @@ class _SelectableTextDemoState extends State { Widget _buildTitle(String title) { return Text( title, - style: TextStyle( - color: const Color(0xFF444444), + style: const TextStyle( + color: Color(0xFF444444), fontSize: 32, ), ); @@ -144,21 +149,21 @@ class _SelectableTextDemoState extends State { children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( + decoration: const BoxDecoration( color: Colors.red, borderRadius: BorderRadius.vertical( top: Radius.circular(4), )), child: Text( title, - style: TextStyle( + style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), - SizedBox(height: 4), + const SizedBox(height: 4), demo, ], ); diff --git a/super_editor/example/lib/demos/demo_switch_document_content.dart b/super_editor/example/lib/demos/demo_switch_document_content.dart index a32cb989e2..20c1507f0a 100644 --- a/super_editor/example/lib/demos/demo_switch_document_content.dart +++ b/super_editor/example/lib/demos/demo_switch_document_content.dart @@ -65,16 +65,16 @@ class _SwitchDocumentDemoState extends State { _activeDocumentEditor = _docEditor1; }); }, - child: Text('Document 1'), + child: const Text('Document 1'), ), - SizedBox(width: 24), + const SizedBox(width: 24), TextButton( onPressed: () { setState(() { _activeDocumentEditor = _docEditor2; }); }, - child: Text('Document 2'), + child: const Text('Document 2'), ), ], ); diff --git a/super_editor/example/lib/demos/example_editor/_toolbar.dart b/super_editor/example/lib/demos/example_editor/_toolbar.dart index 083fdfd07c..b7d1c32159 100644 --- a/super_editor/example/lib/demos/example_editor/_toolbar.dart +++ b/super_editor/example/lib/demos/example_editor/_toolbar.dart @@ -76,7 +76,8 @@ class _EditorToolbarState extends State { return false; } - final selectedNode = widget.editor!.document.getNodeById(selection.extent.nodeId); + final selectedNode = + widget.editor!.document.getNodeById(selection.extent.nodeId); return selectedNode is ParagraphNode || selectedNode is ListItemNode; } @@ -84,7 +85,8 @@ class _EditorToolbarState extends State { /// /// Throws an exception if the currently selected node is not a text node. _TextType _getCurrentTextType() { - final selectedNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId); + final selectedNode = widget.editor!.document + .getNodeById(widget.composer!.selection!.extent.nodeId); if (selectedNode is ParagraphNode) { final type = selectedNode.metadata['blockType']; @@ -100,7 +102,9 @@ class _EditorToolbarState extends State { return _TextType.paragraph; } } else if (selectedNode is ListItemNode) { - return selectedNode.type == ListItemType.ordered ? _TextType.orderedListItem : _TextType.unorderedListItem; + return selectedNode.type == ListItemType.ordered + ? _TextType.orderedListItem + : _TextType.unorderedListItem; } else { throw Exception('Invalid node type: $selectedNode'); } @@ -110,7 +114,8 @@ class _EditorToolbarState extends State { /// /// Throws an exception if the currently selected node is not a text node. TextAlign _getCurrentTextAlignment() { - final selectedNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId); + final selectedNode = widget.editor!.document + .getNodeById(widget.composer!.selection!.extent.nodeId); if (selectedNode is ParagraphNode) { final align = selectedNode.metadata['textAlign']; switch (align) { @@ -126,7 +131,8 @@ class _EditorToolbarState extends State { return TextAlign.left; } } else { - throw Exception('Alignment does not apply to node of type: $selectedNode'); + throw Exception( + 'Alignment does not apply to node of type: $selectedNode'); } } @@ -138,7 +144,8 @@ class _EditorToolbarState extends State { return false; } - final selectedNode = widget.editor!.document.getNodeById(selection.extent.nodeId); + final selectedNode = + widget.editor!.document.getNodeById(selection.extent.nodeId); return selectedNode is ParagraphNode; } @@ -159,7 +166,9 @@ class _EditorToolbarState extends State { widget.editor!.executeCommand( ChangeListItemTypeCommand( nodeId: widget.composer!.selection!.extent.nodeId, - newType: newType == _TextType.orderedListItem ? ListItemType.ordered : ListItemType.unordered, + newType: newType == _TextType.orderedListItem + ? ListItemType.ordered + : ListItemType.unordered, ), ); } else if (_isListItem(existingTextType) && !_isListItem(newType)) { @@ -175,20 +184,25 @@ class _EditorToolbarState extends State { widget.editor!.executeCommand( ConvertParagraphToListItemCommand( nodeId: widget.composer!.selection!.extent.nodeId, - type: newType == _TextType.orderedListItem ? ListItemType.ordered : ListItemType.unordered, + type: newType == _TextType.orderedListItem + ? ListItemType.ordered + : ListItemType.unordered, ), ); } else { // Apply a new block type to an existing paragraph node. - final existingNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId)!; - (existingNode as ParagraphNode).metadata['blockType'] = _getBlockTypeAttribution(newType); + final existingNode = widget.editor!.document + .getNodeById(widget.composer!.selection!.extent.nodeId)!; + (existingNode as ParagraphNode).metadata['blockType'] = + _getBlockTypeAttribution(newType); } } /// Returns true if the given [_TextType] represents an /// ordered or unordered list item, returns false otherwise. bool _isListItem(_TextType? type) { - return type == _TextType.orderedListItem || type == _TextType.unorderedListItem; + return type == _TextType.orderedListItem || + type == _TextType.unorderedListItem; } /// Returns the text [Attribution] associated with the given @@ -260,13 +274,16 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = + TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document + .getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final overlappingLinkAttributions = text.getAttributionSpansInRange( - attributionFilter: (Attribution attribution) => attribution is LinkAttribution, + attributionFilter: (Attribution attribution) => + attribution is LinkAttribution, range: selectionRange, ); @@ -281,13 +298,16 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = + TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document + .getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final overlappingLinkAttributions = text.getAttributionSpansInRange( - attributionFilter: (Attribution attribution) => attribution is LinkAttribution, + attributionFilter: (Attribution attribution) => + attribution is LinkAttribution, range: selectionRange, ); @@ -300,8 +320,10 @@ class _EditorToolbarState extends State { // The selected text contains one other link. final overlappingLinkSpan = overlappingLinkAttributions.first; final isLinkSelectionOnTrailingEdge = - (overlappingLinkSpan.start >= selectionRange.start && overlappingLinkSpan.start <= selectionRange.end) || - (overlappingLinkSpan.end >= selectionRange.start && overlappingLinkSpan.end <= selectionRange.end); + (overlappingLinkSpan.start >= selectionRange.start && + overlappingLinkSpan.start <= selectionRange.end) || + (overlappingLinkSpan.end >= selectionRange.start && + overlappingLinkSpan.end <= selectionRange.end); if (isLinkSelectionOnTrailingEdge) { // The selected text covers the beginning, or the end, or the entire @@ -312,7 +334,8 @@ class _EditorToolbarState extends State { // the entire link attribution. text.removeAttribution( overlappingLinkSpan.attribution, - TextRange(start: overlappingLinkSpan.start, end: overlappingLinkSpan.end), + TextRange( + start: overlappingLinkSpan.start, end: overlappingLinkSpan.end), ); } } else { @@ -334,9 +357,11 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = + TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document + .getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final trimmedRange = _trimTextRangeWhitespace(text, selectionRange); @@ -351,7 +376,8 @@ class _EditorToolbarState extends State { _urlController!.clear(); setState(() { _showUrlField = false; - _urlFocusNode!.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild); + _urlFocusNode! + .unfocus(disposition: UnfocusDisposition.previouslyFocusedChild); widget.closeToolbar(); }); } @@ -397,8 +423,9 @@ class _EditorToolbarState extends State { break; } - final selectedNode = - widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId) as ParagraphNode; + final selectedNode = widget.editor!.document + .getNodeById(widget.composer!.selection!.extent.nodeId) + as ParagraphNode; selectedNode.metadata['textAlign'] = newAlignmentValue; } @@ -431,7 +458,7 @@ class _EditorToolbarState extends State { if (widget.anchor.value == null || widget.composer!.selection == null) { // When no anchor position is available, or the user hasn't // selected any text, show nothing. - return SizedBox(); + return const SizedBox(); } return SizedBox.expand( @@ -452,7 +479,7 @@ class _EditorToolbarState extends State { left: widget.anchor.value!.dx, top: widget.anchor.value!.dy, child: FractionalTranslation( - translation: Offset(-0.5, -1.4), + translation: const Offset(-0.5, -1.4), child: _buildToolbar(), ), ), @@ -465,7 +492,7 @@ class _EditorToolbarState extends State { Widget _buildToolbar() { return Material( - shape: StadiumBorder(), + shape: const StadiumBorder(), elevation: 5, clipBehavior: Clip.hardEdge, child: SizedBox( @@ -489,12 +516,12 @@ class _EditorToolbarState extends State { ), )) .toList(), - icon: Icon(Icons.arrow_drop_down), - style: TextStyle( + icon: const Icon(Icons.arrow_drop_down), + style: const TextStyle( color: Colors.black, fontSize: 12, ), - underline: SizedBox(), + underline: const SizedBox(), elevation: 0, itemHeight: 48, onChanged: _convertTextToNewType, @@ -505,7 +532,7 @@ class _EditorToolbarState extends State { Center( child: IconButton( onPressed: _toggleBold, - icon: Icon(Icons.format_bold), + icon: const Icon(Icons.format_bold), splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelBold, ), @@ -513,7 +540,7 @@ class _EditorToolbarState extends State { Center( child: IconButton( onPressed: _toggleItalics, - icon: Icon(Icons.format_italic), + icon: const Icon(Icons.format_italic), splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelItalics, ), @@ -521,7 +548,7 @@ class _EditorToolbarState extends State { Center( child: IconButton( onPressed: _toggleStrikethrough, - icon: Icon(Icons.strikethrough_s), + icon: const Icon(Icons.strikethrough_s), splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelStrikethrough, ), @@ -529,8 +556,10 @@ class _EditorToolbarState extends State { Center( child: IconButton( onPressed: _areMultipleLinksSelected() ? null : _onLinkPressed, - icon: Icon(Icons.link), - color: _isSingleLinkSelected() ? const Color(0xFF007AFF) : IconTheme.of(context).color, + icon: const Icon(Icons.link), + color: _isSingleLinkSelected() + ? const Color(0xFF007AFF) + : IconTheme.of(context).color, splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelLink, ), @@ -543,7 +572,12 @@ class _EditorToolbarState extends State { message: AppLocalizations.of(context)!.labelTextAlignment, child: DropdownButton( value: _getCurrentTextAlignment(), - items: [TextAlign.left, TextAlign.center, TextAlign.right, TextAlign.justify] + items: [ + TextAlign.left, + TextAlign.center, + TextAlign.right, + TextAlign.justify + ] .map((textAlign) => DropdownMenuItem( value: textAlign, child: Padding( @@ -552,12 +586,12 @@ class _EditorToolbarState extends State { ), )) .toList(), - icon: Icon(Icons.arrow_drop_down), - style: TextStyle( + icon: const Icon(Icons.arrow_drop_down), + style: const TextStyle( color: Colors.black, fontSize: 12, ), - underline: SizedBox(), + underline: const SizedBox(), elevation: 0, itemHeight: 48, onChanged: _changeAlignment, @@ -568,7 +602,7 @@ class _EditorToolbarState extends State { Center( child: IconButton( onPressed: () {}, - icon: Icon(Icons.more_vert), + icon: const Icon(Icons.more_vert), splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelMoreOptions, ), @@ -581,7 +615,7 @@ class _EditorToolbarState extends State { Widget _buildUrlField() { return Material( - shape: StadiumBorder(), + shape: const StadiumBorder(), elevation: 5, clipBehavior: Clip.hardEdge, child: Container( @@ -594,7 +628,7 @@ class _EditorToolbarState extends State { child: TextField( focusNode: _urlFocusNode, controller: _urlController, - decoration: InputDecoration( + decoration: const InputDecoration( hintText: 'enter url...', border: InputBorder.none, ), @@ -602,7 +636,7 @@ class _EditorToolbarState extends State { ), ), IconButton( - icon: Icon(Icons.close), + icon: const Icon(Icons.close), iconSize: 20, splashRadius: 16, padding: EdgeInsets.zero, diff --git a/super_editor/example/lib/demos/sliver_example_editor.dart b/super_editor/example/lib/demos/sliver_example_editor.dart index 0e2443468c..146cb70bc2 100644 --- a/super_editor/example/lib/demos/sliver_example_editor.dart +++ b/super_editor/example/lib/demos/sliver_example_editor.dart @@ -32,11 +32,11 @@ class _SliverExampleEditorState extends State { return CustomScrollView( slivers: [ SliverAppBar( - title: Text( + title: const Text( 'Rich Text Editor Sliver Example', ), expandedHeight: 200.0, - leading: SizedBox(), + leading: const SizedBox(), flexibleSpace: FlexibleSpaceBar( background: Image.network( 'https://i.imgur.com/fSZwM7G.jpg', @@ -44,7 +44,7 @@ class _SliverExampleEditorState extends State { ), ), ), - SliverToBoxAdapter( + const SliverToBoxAdapter( child: Text( 'Lorem Ipsum Dolor', style: TextStyle( @@ -73,7 +73,7 @@ class _SliverExampleEditorState extends State { content: Text( 'SliverList element tapped with index $index.', ), - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), ), ); }, diff --git a/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart b/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart index 5f09645d4e..d3651292ae 100644 --- a/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart @@ -15,7 +15,8 @@ class EmojisTextFieldDemo extends StatefulWidget { _EmojisTextFieldDemoState createState() => _EmojisTextFieldDemoState(); } -class _EmojisTextFieldDemoState extends State with TickerProviderStateMixin { +class _EmojisTextFieldDemoState extends State + with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController(); GlobalKey? _textKey; @@ -48,7 +49,7 @@ class _EmojisTextFieldDemoState extends State with TickerPr void _startDemo() { _textFieldController - ..selection = TextSelection.collapsed(offset: 0) + ..selection = const TextSelection.collapsed(offset: 0) ..text = AttributedText( text: 'turtle 🐢 bomb 💣 skull ☠', ); @@ -56,14 +57,15 @@ class _EmojisTextFieldDemoState extends State with TickerPr if (widget.direction == TextAffinity.upstream) { // simulate pressing backspace _demoRobot - ..insertCaretAt(TextPosition(offset: _textFieldController.text.text.length)) + ..insertCaretAt( + TextPosition(offset: _textFieldController.text.text.length)) ..pause(const Duration(seconds: 1)) ..backspaceCharacters(_textFieldController.text.text.length) ..start(); } else { // simulate pressing delete _demoRobot - ..insertCaretAt(TextPosition(offset: 0)) + ..insertCaretAt(const TextPosition(offset: 0)) ..pause(const Duration(seconds: 1)) ..deleteCharacters(_textFieldController.text.text.length) ..start(); @@ -99,13 +101,16 @@ class _EmojisTextFieldDemoState extends State with TickerPr key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, + color: _focusNode!.hasFocus + ? Colors.blue + : Colors.grey.shade300, width: 1, ), ), @@ -113,7 +118,7 @@ class _EmojisTextFieldDemoState extends State with TickerPr ); }, hintBuilder: (context) { - return Text( + return const Text( 'enter some text', style: TextStyle( color: Colors.grey, @@ -126,10 +131,10 @@ class _EmojisTextFieldDemoState extends State with TickerPr ), ), ), - SizedBox(height: 16), + const SizedBox(height: 16), ElevatedButton( onPressed: _restartDemo, - child: Text('Restart Demo'), + child: const Text('Restart Demo'), ), ], ), diff --git a/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart index ca4b1c43f6..3d6cf84319 100644 --- a/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart @@ -5,10 +5,12 @@ import '_robot.dart'; class ExpandingMultiLineTextFieldDemo extends StatefulWidget { @override - _ExpandingMultiLineTextFieldDemoState createState() => _ExpandingMultiLineTextFieldDemoState(); + _ExpandingMultiLineTextFieldDemoState createState() => + _ExpandingMultiLineTextFieldDemoState(); } -class _ExpandingMultiLineTextFieldDemoState extends State +class _ExpandingMultiLineTextFieldDemoState + extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( @@ -53,7 +55,7 @@ class _ExpandingMultiLineTextFieldDemoState extends State _InteractiveTextFieldDemoState(); + _InteractiveTextFieldDemoState createState() => + _InteractiveTextFieldDemoState(); } class _InteractiveTextFieldDemoState extends State { final _textFieldController = AttributedTextEditingController( text: AttributedText( - text: 'Super Editor is an open source text editor for Flutter projects.', - spans: AttributedSpans(attributions: [ - SpanMarker(attribution: brandAttribution, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: brandAttribution, offset: 11, markerType: SpanMarkerType.end), - SpanMarker(attribution: flutterAttribution, offset: 47, markerType: SpanMarkerType.start), - SpanMarker(attribution: flutterAttribution, offset: 53, markerType: SpanMarkerType.end), + text: + 'Super Editor is an open source text editor for Flutter projects.', + spans: AttributedSpans(attributions: const [ + SpanMarker( + attribution: brandAttribution, + offset: 0, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: brandAttribution, + offset: 11, + markerType: SpanMarkerType.end), + SpanMarker( + attribution: flutterAttribution, + offset: 47, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: flutterAttribution, + offset: 53, + markerType: SpanMarkerType.end), ])), ); @@ -39,8 +53,8 @@ class _InteractiveTextFieldDemoState extends State { super.dispose(); } - void _onRightClick( - BuildContext textFieldContext, AttributedTextEditingController textController, Offset localOffset) { + void _onRightClick(BuildContext textFieldContext, + AttributedTextEditingController textController, Offset localOffset) { // Only show menu if some text is selected if (textController.selection.isCollapsed) { return; @@ -49,7 +63,8 @@ class _InteractiveTextFieldDemoState extends State { final overlay = Overlay.of(context)!; final overlayBox = overlay.context.findRenderObject() as RenderBox?; final textFieldBox = textFieldContext.findRenderObject() as RenderBox; - _popupOffset = textFieldBox.localToGlobal(localOffset, ancestor: overlayBox); + _popupOffset = + textFieldBox.localToGlobal(localOffset, ancestor: overlayBox); if (_popupEntry == null) { _popupEntry = OverlayEntry(builder: (context) { @@ -84,11 +99,12 @@ class _InteractiveTextFieldDemoState extends State { TextButton( onPressed: () { Clipboard.setData(ClipboardData( - text: textController.selection.textInside(textController.text.text), + text: textController.selection + .textInside(textController.text.text), )); _closePopup(); }, - child: Text('Copy'), + child: const Text('Copy'), ), ], ), @@ -136,13 +152,16 @@ class _InteractiveTextFieldDemoState extends State { textController: _textFieldController, focusNode: _focusNode, textStyleBuilder: _textStyleBuilder, - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, + color: _focusNode!.hasFocus + ? Colors.blue + : Colors.grey.shade300, width: 1, ), ), @@ -150,7 +169,7 @@ class _InteractiveTextFieldDemoState extends State { ); }, hintBuilder: (context) { - return Text( + return const Text( 'enter some text', style: TextStyle( color: Colors.grey, @@ -170,7 +189,7 @@ class _InteractiveTextFieldDemoState extends State { } TextStyle _textStyleBuilder(Set attributions) { - TextStyle textStyle = TextStyle( + TextStyle textStyle = const TextStyle( color: Colors.black, fontSize: 14, ); diff --git a/super_editor/example/lib/demos/supertextfield/_robot.dart b/super_editor/example/lib/demos/supertextfield/_robot.dart index ef684facb0..9be8867cda 100644 --- a/super_editor/example/lib/demos/supertextfield/_robot.dart +++ b/super_editor/example/lib/demos/supertextfield/_robot.dart @@ -59,7 +59,8 @@ class TextFieldDemoRobot { } Future deselect() async { - _commands.add(SelectTextCommand(selection: TextSelection.collapsed(offset: -1))); + _commands.add(SelectTextCommand( + selection: const TextSelection.collapsed(offset: -1))); } Future pause(Duration duration) async { @@ -132,7 +133,8 @@ class TypeTextCommand implements RobotCommand { GlobalKey? textKey, ) async { if (textController.selection.extentOffset == -1) { - print('Can\'t type text because the text field doesn\'t have a valid selection.'); + print( + 'Can\'t type text because the text field doesn\'t have a valid selection.'); return; } @@ -149,15 +151,19 @@ class TypeTextCommand implements RobotCommand { } } - void _typeCharacter(AttributedTextEditingController textController, int offset) { + void _typeCharacter( + AttributedTextEditingController textController, int offset) { textController.text = textController.text.insertString( - textToInsert: textToType.text[offset], // TODO: support insertion of attributed text + textToInsert: + textToType.text[offset], // TODO: support insertion of attributed text startOffset: textController.selection.extentOffset, ); final previousSelection = textController.selection; textController.selection = TextSelection( - baseOffset: previousSelection.isCollapsed ? previousSelection.extentOffset + 1 : previousSelection.baseOffset, + baseOffset: previousSelection.isCollapsed + ? previousSelection.extentOffset + 1 + : previousSelection.baseOffset, extentOffset: previousSelection.extentOffset + 1, ); } @@ -215,7 +221,8 @@ class DeleteCharactersCommand implements RobotCommand { GlobalKey? textKey, ) async { if (textController.selection.extentOffset == -1) { - print('Can\'t delete characters because the text field doesn\'t have a valid selection.'); + print( + 'Can\'t delete characters because the text field doesn\'t have a valid selection.'); return; } @@ -224,7 +231,8 @@ class DeleteCharactersCommand implements RobotCommand { int currentOffset = textController.selection.extentOffset; final finalLength = textController.text.text.length - characterCount; while (textController.text.text.length > finalLength) { - final codePointsDeleted = _deleteCharacter(textController, currentOffset, direction); + final codePointsDeleted = + _deleteCharacter(textController, currentOffset, direction); if (direction == TextAffinity.upstream) { currentOffset -= codePointsDeleted; } @@ -237,7 +245,8 @@ class DeleteCharactersCommand implements RobotCommand { } } - int _deleteCharacter(AttributedTextEditingController textController, int offset, TextAffinity direction) { + int _deleteCharacter(AttributedTextEditingController textController, + int offset, TextAffinity direction) { int deleteStartIndex; int deleteEndIndex; int deletedCodePointCount; @@ -251,7 +260,8 @@ class DeleteCharactersCommand implements RobotCommand { newSelectionIndex = deleteStartIndex; } else { // Delete the character before the offset - deleteStartIndex = getCharacterStartBounds(textController.text.text, offset); + deleteStartIndex = + getCharacterStartBounds(textController.text.text, offset); deleteEndIndex = offset + 1; deletedCodePointCount = offset - deleteStartIndex; newSelectionIndex = deleteStartIndex; @@ -262,7 +272,8 @@ class DeleteCharactersCommand implements RobotCommand { endOffset: deleteEndIndex, ); - textController.selection = TextSelection.collapsed(offset: newSelectionIndex); + textController.selection = + TextSelection.collapsed(offset: newSelectionIndex); return deletedCodePointCount; } @@ -312,7 +323,8 @@ class InsertCaretCommand implements RobotCommand { GlobalKey? textKey, ) async { focusNode!.requestFocus(); - textController.selection = TextSelection.collapsed(offset: caretPosition.offset); + textController.selection = + TextSelection.collapsed(offset: caretPosition.offset); } @override diff --git a/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart index 20a38eac05..cd9b5869db 100644 --- a/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart @@ -5,10 +5,12 @@ import '_robot.dart'; class SingleLineTextFieldDemo extends StatefulWidget { @override - _SingleLineTextFieldDemoState createState() => _SingleLineTextFieldDemoState(); + _SingleLineTextFieldDemoState createState() => + _SingleLineTextFieldDemoState(); } -class _SingleLineTextFieldDemoState extends State with TickerProviderStateMixin { +class _SingleLineTextFieldDemoState extends State + with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( // text: @@ -52,10 +54,12 @@ class _SingleLineTextFieldDemoState extends State with void _startDemo() { _textFieldController - ..selection = TextSelection.collapsed(offset: 0) + ..selection = const TextSelection.collapsed(offset: 0) ..text = AttributedText(); _demoRobot - ..typeText(AttributedText(text: 'Hello World! This is a robot typing some text into a SuperTextField.')) + ..typeText(AttributedText( + text: + 'Hello World! This is a robot typing some text into a SuperTextField.')) ..start(); } @@ -88,13 +92,16 @@ class _SingleLineTextFieldDemoState extends State with key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, + color: _focusNode!.hasFocus + ? Colors.blue + : Colors.grey.shade300, width: 1, ), ), @@ -102,7 +109,7 @@ class _SingleLineTextFieldDemoState extends State with ); }, hintBuilder: (context) { - return Text( + return const Text( 'enter some text', style: TextStyle( color: Colors.grey, @@ -115,10 +122,10 @@ class _SingleLineTextFieldDemoState extends State with ), ), ), - SizedBox(height: 16), + const SizedBox(height: 16), ElevatedButton( onPressed: _restartDemo, - child: Text('Restart Demo'), + child: const Text('Restart Demo'), ), ], ), diff --git a/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart index f72e6bd3b7..6e9b686fd4 100644 --- a/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart @@ -5,10 +5,12 @@ import '_robot.dart'; class StaticMultiLineTextFieldDemo extends StatefulWidget { @override - _StaticMultiLineTextFieldDemoState createState() => _StaticMultiLineTextFieldDemoState(); + _StaticMultiLineTextFieldDemoState createState() => + _StaticMultiLineTextFieldDemoState(); } -class _StaticMultiLineTextFieldDemoState extends State with TickerProviderStateMixin { +class _StaticMultiLineTextFieldDemoState + extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( // text: @@ -52,7 +54,7 @@ class _StaticMultiLineTextFieldDemoState extends State { label: 'Backspace emojis', onPressed: () { setState(() { - _demoBuilder = (_) => EmojisTextFieldDemo( + _demoBuilder = (_) => const EmojisTextFieldDemo( key: ValueKey('backspace'), direction: TextAffinity.upstream, ); @@ -84,7 +84,7 @@ class _TextFieldDemoState extends State { label: 'Delete emojis', onPressed: () { setState(() { - _demoBuilder = (_) => EmojisTextFieldDemo( + _demoBuilder = (_) => const EmojisTextFieldDemo( key: ValueKey('delete'), direction: TextAffinity.downstream, ); diff --git a/super_editor/example/lib/main.dart b/super_editor/example/lib/main.dart index 94189d7e9e..6c79dcadad 100644 --- a/super_editor/example/lib/main.dart +++ b/super_editor/example/lib/main.dart @@ -27,11 +27,11 @@ class SuperEditorDemoApp extends StatelessWidget { primarySwatch: Colors.red, ), home: HomeScreen(), - supportedLocales: [ - const Locale('en', ''), - const Locale('es', ''), + supportedLocales: const [ + Locale('en', ''), + Locale('es', ''), ], - localizationsDelegates: [ + localizationsDelegates: const [ ...AppLocalizations.localizationsDelegates, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, @@ -98,7 +98,7 @@ class _HomeScreenState extends State { backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( - icon: Icon(Icons.menu), + icon: const Icon(Icons.menu), color: Theme.of(context).colorScheme.onSurface, splashRadius: 24, onPressed: _toggleDrawer, @@ -126,7 +126,7 @@ class _HomeScreenState extends State { }, ), ], - SizedBox(height: 24), + const SizedBox(height: 24), ], ], ), @@ -253,8 +253,8 @@ class _DrawerHeader extends StatelessWidget { padding: const EdgeInsets.only(left: 16, bottom: 4), child: Text( title!, - style: TextStyle( - color: const Color(0xFF444444), + style: const TextStyle( + color: Color(0xFF444444), fontSize: 10, fontWeight: FontWeight.bold, ), @@ -295,18 +295,19 @@ class _DrawerButton extends StatelessWidget { return Colors.transparent; }), // splashFactory: NoSplash.splashFactory, - foregroundColor: - MaterialStateColor.resolveWith((states) => isSelected ? Colors.white : const Color(0xFFBBBBBB)), + foregroundColor: MaterialStateColor.resolveWith((states) => + isSelected ? Colors.white : const Color(0xFFBBBBBB)), elevation: MaterialStateProperty.resolveWith((states) => 0), - padding: MaterialStateProperty.resolveWith((states) => const EdgeInsets.all(16))), + padding: MaterialStateProperty.resolveWith( + (states) => const EdgeInsets.all(16))), onPressed: isSelected ? null : onPressed, child: Row( children: [ - SizedBox(width: 8), + const SizedBox(width: 8), Icon( icon, ), - SizedBox(width: 16), + const SizedBox(width: 16), Expanded( child: Text(title), ), diff --git a/super_editor/example/lib/marketing_video/main_marketing_video.dart b/super_editor/example/lib/marketing_video/main_marketing_video.dart index da5fba2525..aeab7ff28d 100644 --- a/super_editor/example/lib/marketing_video/main_marketing_video.dart +++ b/super_editor/example/lib/marketing_video/main_marketing_video.dart @@ -235,9 +235,9 @@ TextStyle _textStyleBuilder(Set attributions) { return textStyle; } -final superlistBrandAttribution = NamedAttribution('superlist_brand'); -final titleAttribution = NamedAttribution('titleAttribution'); -final headerAttribution = NamedAttribution('header'); +const superlistBrandAttribution = NamedAttribution('superlist_brand'); +const titleAttribution = NamedAttribution('titleAttribution'); +const headerAttribution = NamedAttribution('header'); class DocumentEditingRobot { DocumentEditingRobot({ @@ -247,15 +247,18 @@ class DocumentEditingRobot { int? randomSeed, }) : _editor = editor, _composer = composer, - _editorOps = - CommonEditorOperations(editor: editor, composer: composer, documentLayoutResolver: documentLayoutFinder as DocumentLayout Function()), + _editorOps = CommonEditorOperations( + editor: editor, + composer: composer, + documentLayoutResolver: + documentLayoutFinder as DocumentLayout Function()), _random = Random(randomSeed); final DocumentEditor _editor; final DocumentComposer _composer; final CommonEditorOperations _editorOps; final _actionQueue = []; - final _random; + final Random _random; void placeCaret(DocumentPosition position) { _actionQueue.add( @@ -354,7 +357,8 @@ class DocumentEditingRobot { _editorOps.insertCharacter(character); if (character == ' ') { - _editorOps.convertParagraphByPatternMatching(_composer.selection!.extent.nodeId); + _editorOps.convertParagraphByPatternMatching( + _composer.selection!.extent.nodeId); } }, ), @@ -370,7 +374,8 @@ class DocumentEditingRobot { _editorOps.insertCharacter(character); if (character == ' ') { - _editorOps.convertParagraphByPatternMatching(_composer.selection!.extent.nodeId); + _editorOps.convertParagraphByPatternMatching( + _composer.selection!.extent.nodeId); } }, true, @@ -447,7 +452,9 @@ class DocumentEditingRobot { } Duration _randomWaitPeriod([bool fastMode = false]) { - return Duration(milliseconds: _random.nextInt(fastMode ? 45 : 200) + (fastMode ? 5 : 50)); + return Duration( + milliseconds: + _random.nextInt(fastMode ? 45 : 200) + (fastMode ? 5 : 50)); } Future start() async { diff --git a/super_editor/example/pubspec.lock b/super_editor/example/pubspec.lock index a98b9a3ff7..0793b42fda 100644 --- a/super_editor/example/pubspec.lock +++ b/super_editor/example/pubspec.lock @@ -90,6 +90,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: transitive + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" flutter_localizations: dependency: "direct main" description: flutter @@ -135,6 +142,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" markdown: dependency: transitive description: diff --git a/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart b/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart index d3f4d10d28..2ec5599eae 100644 --- a/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart +++ b/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart @@ -10,8 +10,18 @@ void main() { spans.addAttribution(newAttribution: 'bold', start: 0, end: 16); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 0, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 16, + markerType: SpanMarkerType.end)); }); test('applies attribution to beginning of span', () { @@ -20,8 +30,18 @@ void main() { spans.addAttribution(newAttribution: 'bold', start: 0, end: 7); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 0, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 7, + markerType: SpanMarkerType.end)); }); test('applies attribution to inner span', () { @@ -30,8 +50,18 @@ void main() { spans.addAttribution(newAttribution: 'bold', start: 2, end: 7); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 7, + markerType: SpanMarkerType.end)); }); test('applies attribution to end of span', () { @@ -40,8 +70,18 @@ void main() { spans.addAttribution(newAttribution: 'bold', start: 7, end: 16); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 7, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 16, + markerType: SpanMarkerType.end)); }); test('applies exotic span', () { @@ -54,10 +94,18 @@ void main() { spans.addAttribution(newAttribution: linkAttribution, start: 2, end: 7); expect(spans.attributions.length, 2); - expect(spans.attributions[0], - SpanMarker(attribution: linkAttribution, offset: 2, markerType: SpanMarkerType.start)); expect( - spans.attributions[1], SpanMarker(attribution: linkAttribution, offset: 7, markerType: SpanMarkerType.end)); + spans.attributions[0], + SpanMarker( + attribution: linkAttribution, + offset: 2, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + SpanMarker( + attribution: linkAttribution, + offset: 7, + markerType: SpanMarkerType.end)); expect(spans.getAllAttributionsAt(4).first, equals(linkAttribution)); }); @@ -65,8 +113,12 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 0, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); @@ -79,8 +131,12 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); @@ -93,74 +149,144 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); spans.removeAttribution(attributionToRemove: 'bold', start: 2, end: 4); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 5, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 5, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 7, + markerType: SpanMarkerType.end)); }); test('removes attribution from partial inner span', () { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); spans.removeAttribution(attributionToRemove: 'bold', start: 4, end: 5); expect(spans.attributions.length, 4); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 3, markerType: SpanMarkerType.end)); - expect(spans.attributions[2], SpanMarker(attribution: 'bold', offset: 6, markerType: SpanMarkerType.start)); - expect(spans.attributions[3], SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 3, + markerType: SpanMarkerType.end)); + expect( + spans.attributions[2], + const SpanMarker( + attribution: 'bold', + offset: 6, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[3], + const SpanMarker( + attribution: 'bold', + offset: 7, + markerType: SpanMarkerType.end)); }); test('removes attribution from partial ending span', () { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); spans.removeAttribution(attributionToRemove: 'bold', start: 5, end: 7); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 4, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 2, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 4, + markerType: SpanMarkerType.end)); }); test('applies attribution when mixed span is toggled', () { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 8, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 8, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); spans.toggleAttribution(attribution: 'bold', start: 0, end: 16); expect(spans.attributions.length, 2); - expect(spans.attributions[0], SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); - expect(spans.attributions[1], SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); + expect( + spans.attributions[0], + const SpanMarker( + attribution: 'bold', + offset: 0, + markerType: SpanMarkerType.start)); + expect( + spans.attributions[1], + const SpanMarker( + attribution: 'bold', + offset: 16, + markerType: SpanMarkerType.end)); }); test('removes attribution when contiguous span is toggled', () { final spans = AttributedSpans( length: 17, attributions: [ - SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: 'bold', + offset: 0, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); @@ -278,18 +404,26 @@ class _ExpectedSpans { late List _combinedSpans; void expectSpans(AttributedSpans spans) { - for (int characterIndex = 0; characterIndex < _combinedSpans.length; ++characterIndex) { - for (int attributionIndex = 0; attributionIndex < _combinedSpans[characterIndex].length; ++attributionIndex) { + for (int characterIndex = 0; + characterIndex < _combinedSpans.length; + ++characterIndex) { + for (int attributionIndex = 0; + attributionIndex < _combinedSpans[characterIndex].length; + ++attributionIndex) { print('Checking character $characterIndex'); // The attribution name is just a letter, like 'b', 'i', or 's'. - final attributionName = _combinedSpans[characterIndex][attributionIndex]; + final attributionName = + _combinedSpans[characterIndex][attributionIndex]; print(' - looking for attribution: "$attributionName"'); if (attributionName == '_') { print(' - skipping empty template character'); continue; } - expect(spans.hasAttributionAt(characterIndex, attribution: attributionName), true); + expect( + spans.hasAttributionAt(characterIndex, + attribution: attributionName), + true); } } } diff --git a/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart b/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart index 406eec3161..4b010e6564 100644 --- a/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart +++ b/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart @@ -16,9 +16,11 @@ void main() { }); test('full-span style', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ - SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 9, markerType: SpanMarkerType.end), + final text = AttributedText(text: 'abcdefghij', attributions: const [ + SpanMarker( + attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 9, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -28,9 +30,11 @@ void main() { }); test('single character style', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ - SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), + final text = AttributedText(text: 'abcdefghij', attributions: const [ + SpanMarker( + attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -44,12 +48,14 @@ void main() { }); test('single character style - reverse order', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ + final text = AttributedText(text: 'abcdefghij', attributions: const [ // Notice that the markers are provided in reverse order: // end then start. Order shouldn't matter within a single // position index. This test ensures that. - SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), - SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), + SpanMarker( + attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -64,7 +70,7 @@ void main() { test('add single character style', () { final text = AttributedText(text: 'abcdefghij'); - text.addAttribution('bold', TextRange(start: 1, end: 1)); + text.addAttribution('bold', const TextRange(start: 1, end: 1)); final textSpan = text.computeTextSpan(_styleBuilder); expect(textSpan.text, null); @@ -77,9 +83,11 @@ void main() { }); test('partial style', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end), + final text = AttributedText(text: 'abcdefghij', attributions: const [ + SpanMarker( + attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 7, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -92,11 +100,17 @@ void main() { }); test('non-mingled varying styles', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ - SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 4, markerType: SpanMarkerType.end), - SpanMarker(attribution: 'italics', offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'italics', offset: 9, markerType: SpanMarkerType.end), + final text = AttributedText(text: 'abcdefghij', attributions: const [ + SpanMarker( + attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 4, markerType: SpanMarkerType.end), + SpanMarker( + attribution: 'italics', + offset: 5, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'italics', offset: 9, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -111,11 +125,17 @@ void main() { }); test('intermingled varying styles', () { - final text = AttributedText(text: 'abcdefghij', attributions: [ - SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'italics', offset: 4, markerType: SpanMarkerType.start), - SpanMarker(attribution: 'bold', offset: 5, markerType: SpanMarkerType.end), - SpanMarker(attribution: 'italics', offset: 7, markerType: SpanMarkerType.end), + final text = AttributedText(text: 'abcdefghij', attributions: const [ + SpanMarker( + attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'italics', + offset: 4, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: 'bold', offset: 5, markerType: SpanMarkerType.end), + SpanMarker( + attribution: 'italics', offset: 7, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); diff --git a/super_editor/lib/src/core/document_editor.dart b/super_editor/lib/src/core/document_editor.dart index 9ba5504e68..379e9b889d 100644 --- a/super_editor/lib/src/core/document_editor.dart +++ b/super_editor/lib/src/core/document_editor.dart @@ -13,7 +13,7 @@ import 'document.dart'; /// can be event-sourced, allowing for undo/redo behavior. // TODO: design and implement comprehensive event-sourced editing API (#49) class DocumentEditor { - static final Uuid _uuid = Uuid(); + static const Uuid _uuid = Uuid(); /// Generates a new ID for a `DocumentNode`. /// @@ -139,7 +139,9 @@ class MutableDocument with ChangeNotifier implements Document { @override DocumentNode? getNodeAfter(DocumentNode node) { final nodeIndex = getNodeIndex(node); - return nodeIndex >= 0 && nodeIndex < nodes.length - 1 ? getNodeAt(nodeIndex + 1) : null; + return nodeIndex >= 0 && nodeIndex < nodes.length - 1 + ? getNodeAt(nodeIndex + 1) + : null; } @override @@ -147,7 +149,8 @@ class MutableDocument with ChangeNotifier implements Document { _nodes.firstWhereOrNull((element) => element.id == position.nodeId); @override - DocumentRange getRangeBetween(DocumentPosition position1, DocumentPosition position2) { + DocumentRange getRangeBetween( + DocumentPosition position1, DocumentPosition position2) { final node1 = getNode(position1); if (node1 == null) { throw Exception('No such position in document: $position1'); @@ -167,7 +170,8 @@ class MutableDocument with ChangeNotifier implements Document { } @override - List getNodesInside(DocumentPosition position1, DocumentPosition position2) { + List getNodesInside( + DocumentPosition position1, DocumentPosition position2) { final node1 = getNode(position1); if (node1 == null) { throw Exception('No such position in document: $position1'); @@ -259,7 +263,9 @@ class MutableDocument with ChangeNotifier implements Document { @override bool operator ==(Object other) => identical(this, other) || - other is MutableDocument && runtimeType == other.runtimeType && DeepCollectionEquality().equals(_nodes, nodes); + other is MutableDocument && + runtimeType == other.runtimeType && + const DeepCollectionEquality().equals(_nodes, nodes); @override int get hashCode => _nodes.hashCode; diff --git a/super_editor/lib/src/default_editor/blockquote.dart b/super_editor/lib/src/default_editor/blockquote.dart index d76b64ba0d..eb61b372cd 100644 --- a/super_editor/lib/src/default_editor/blockquote.dart +++ b/super_editor/lib/src/default_editor/blockquote.dart @@ -16,6 +16,7 @@ import 'paragraph.dart'; import 'styles.dart'; import 'text.dart'; +// ignore: unused_element final _log = Logger(scope: 'blockquote.dart'); /// Displays a blockquote in a document. @@ -101,8 +102,10 @@ ExecutionInstruction insertNewlineInBlockquote({ return ExecutionInstruction.continueExecution; } - final baseNode = editContext.editor.document.getNodeById(editContext.composer.selection!.base.nodeId)!; - final extentNode = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId)!; + final baseNode = editContext.editor.document + .getNodeById(editContext.composer.selection!.base.nodeId)!; + final extentNode = editContext.editor.document + .getNodeById(editContext.composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return ExecutionInstruction.continueExecution; } @@ -114,7 +117,9 @@ ExecutionInstruction insertNewlineInBlockquote({ } final didInsertNewline = editContext.commonOps.insertPlainText('\n'); - return didInsertNewline ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didInsertNewline + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction splitBlockquoteWhenEnterPressed({ @@ -129,8 +134,10 @@ ExecutionInstruction splitBlockquoteWhenEnterPressed({ return ExecutionInstruction.continueExecution; } - final baseNode = editContext.editor.document.getNodeById(editContext.composer.selection!.base.nodeId)!; - final extentNode = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId)!; + final baseNode = editContext.editor.document + .getNodeById(editContext.composer.selection!.base.nodeId)!; + final extentNode = editContext.editor.document + .getNodeById(editContext.composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return ExecutionInstruction.continueExecution; } @@ -142,7 +149,9 @@ ExecutionInstruction splitBlockquoteWhenEnterPressed({ } final didSplit = editContext.commonOps.insertBlockLevelNewline(); - return didSplit ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didSplit + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } class SplitBlockquoteCommand implements EditorCommand { @@ -162,7 +171,9 @@ class SplitBlockquoteCommand implements EditorCommand { final blockquote = node as ParagraphNode; final text = blockquote.text; final startText = text.copyText(0, splitPosition.offset); - final endText = splitPosition.offset < text.text.length ? text.copyText(splitPosition.offset) : AttributedText(); + final endText = splitPosition.offset < text.text.length + ? text.copyText(splitPosition.offset) + : AttributedText(); // Change the current node's content to just the text before the caret. // TODO: figure out how node changes should work in terms of @@ -175,7 +186,8 @@ class SplitBlockquoteCommand implements EditorCommand { final newNode = ParagraphNode( id: newNodeId, text: endText, - metadata: isNewNodeABlockquote ? {'blockType': blockquoteAttribution} : {}, + metadata: + isNewNodeABlockquote ? {'blockType': blockquoteAttribution} : {}, ); // Insert the new node after the current node. @@ -195,16 +207,22 @@ Widget? blockquoteBuilder(ComponentContext componentContext) { return null; } - final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = + componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && + (componentContext.nodeSelection?.isExtent ?? false); return BlockquoteComponent( textKey: componentContext.componentKey, text: blockquoteNode.text, styleBuilder: componentContext.extensions[textStylesExtensionKey], textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .textCaretColor, ); } diff --git a/super_editor/lib/src/default_editor/box_component.dart b/super_editor/lib/src/default_editor/box_component.dart index 0e15451fec..bb81ef0373 100644 --- a/super_editor/lib/src/default_editor/box_component.dart +++ b/super_editor/lib/src/default_editor/box_component.dart @@ -7,6 +7,7 @@ import 'package:super_editor/super_editor.dart'; import '../core/document.dart'; import '../core/document_layout.dart'; +// ignore: unused_element final _log = Logger(scope: 'box_component.dart'); /// Editor layout component that displays content that is either @@ -27,22 +28,24 @@ class BoxComponent extends StatefulWidget { class _BoxComponentState extends State with DocumentComponent { @override BinaryNodePosition getBeginningPosition() { - return BinaryNodePosition.included(); + return const BinaryNodePosition.included(); } @override BinaryNodePosition getBeginningPositionNearX(double x) { - return BinaryNodePosition.included(); + return const BinaryNodePosition.included(); } @override - BinaryNodePosition? movePositionLeft(dynamic currentPosition, [Set? movementModifiers]) { + BinaryNodePosition? movePositionLeft(dynamic currentPosition, + [Set? movementModifiers]) { // BoxComponents don't support internal movement. return null; } @override - BinaryNodePosition? movePositionRight(dynamic currentPosition, [Set? movementModifiers]) { + BinaryNodePosition? movePositionRight(dynamic currentPosition, + [Set? movementModifiers]) { // BoxComponents don't support internal movement. return null; } @@ -62,10 +65,11 @@ class _BoxComponentState extends State with DocumentComponent { @override BinarySelection getCollapsedSelectionAt(nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception('The given nodePosition ($nodePosition) is not compatible with BoxComponent'); + throw Exception( + 'The given nodePosition ($nodePosition) is not compatible with BoxComponent'); } - return BinarySelection.all(); + return const BinarySelection.all(); } @override @@ -75,18 +79,19 @@ class _BoxComponentState extends State with DocumentComponent { @override BinaryNodePosition getEndPosition() { - return BinaryNodePosition.included(); + return const BinaryNodePosition.included(); } @override BinaryNodePosition getEndPositionNearX(double x) { - return BinaryNodePosition.included(); + return const BinaryNodePosition.included(); } @override Offset getOffsetForPosition(nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception('Expected nodePosition of type BinaryPosition but received: $nodePosition'); + throw Exception( + 'Expected nodePosition of type BinaryPosition but received: $nodePosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -96,7 +101,8 @@ class _BoxComponentState extends State with DocumentComponent { @override Rect getRectForPosition(dynamic nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception('Expected nodePosition of type BinaryPosition but received: $nodePosition'); + throw Exception( + 'Expected nodePosition of type BinaryPosition but received: $nodePosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -106,10 +112,12 @@ class _BoxComponentState extends State with DocumentComponent { @override Rect getRectForSelection(dynamic basePosition, dynamic extentPosition) { if (basePosition is! BinaryNodePosition) { - throw Exception('Expected nodePosition of type BinaryPosition but received: $basePosition'); + throw Exception( + 'Expected nodePosition of type BinaryPosition but received: $basePosition'); } if (extentPosition is! BinaryNodePosition) { - throw Exception('Expected nodePosition of type BinaryPosition but received: $extentPosition'); + throw Exception( + 'Expected nodePosition of type BinaryPosition but received: $extentPosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -118,29 +126,33 @@ class _BoxComponentState extends State with DocumentComponent { @override BinaryNodePosition getPositionAtOffset(Offset localOffset) { - return BinaryNodePosition.included(); + return const BinaryNodePosition.included(); } @override - BinarySelection getSelectionBetween({required basePosition, required extentPosition}) { + BinarySelection getSelectionBetween( + {required basePosition, required extentPosition}) { if (basePosition is! BinaryNodePosition) { - throw Exception('The given basePosition ($basePosition) is not compatible with BoxComponent'); + throw Exception( + 'The given basePosition ($basePosition) is not compatible with BoxComponent'); } if (extentPosition is! BinaryNodePosition) { - throw Exception('The given extentPosition ($extentPosition) is not compatible with BoxComponent'); + throw Exception( + 'The given extentPosition ($extentPosition) is not compatible with BoxComponent'); } - return BinarySelection.all(); + return const BinarySelection.all(); } @override - BinarySelection getSelectionInRange(Offset localBaseOffset, Offset localExtentOffset) { - return BinarySelection.all(); + BinarySelection getSelectionInRange( + Offset localBaseOffset, Offset localExtentOffset) { + return const BinarySelection.all(); } @override BinarySelection getSelectionOfEverything() { - return BinarySelection.all(); + return const BinarySelection.all(); } @override @@ -160,7 +172,9 @@ class BinaryNodePosition implements NodePosition { @override bool operator ==(Object other) => identical(this, other) || - other is BinaryNodePosition && runtimeType == other.runtimeType && isIncluded == other.isIncluded; + other is BinaryNodePosition && + runtimeType == other.runtimeType && + isIncluded == other.isIncluded; @override int get hashCode => isIncluded.hashCode; @@ -176,7 +190,8 @@ class BinaryNodePosition implements NodePosition { /// "selection" type. class BinarySelection implements NodeSelection { const BinarySelection.all() : position = const BinaryNodePosition.included(); - const BinarySelection.none() : position = const BinaryNodePosition.notIncluded(); + const BinarySelection.none() + : position = const BinaryNodePosition.notIncluded(); final BinaryNodePosition position; @@ -188,7 +203,9 @@ class BinarySelection implements NodeSelection { @override bool operator ==(Object other) => identical(this, other) || - other is BinarySelection && runtimeType == other.runtimeType && position == other.position; + other is BinarySelection && + runtimeType == other.runtimeType && + position == other.position; @override int get hashCode => position.hashCode; diff --git a/super_editor/lib/src/default_editor/common_editor_operations.dart b/super_editor/lib/src/default_editor/common_editor_operations.dart index a1db49081a..cef7012c46 100644 --- a/super_editor/lib/src/default_editor/common_editor_operations.dart +++ b/super_editor/lib/src/default_editor/common_editor_operations.dart @@ -35,12 +35,10 @@ final _log = Logger(scope: 'common_editor_operations.dart'); /// implemented within [CommonEditorOperations]. class CommonEditorOperations { CommonEditorOperations({ - required DocumentEditor editor, - required DocumentComposer composer, - required DocumentLayoutResolver documentLayoutResolver, - }) : editor = editor, - composer = composer, - documentLayoutResolver = documentLayoutResolver; + required this.editor, + required this.composer, + required this.documentLayoutResolver, + }); // Marked as protected for extension methods and subclasses @protected @@ -63,7 +61,8 @@ class CommonEditorOperations { return false; } - composer.selection = DocumentSelection.collapsed(position: documentPosition); + composer.selection = + DocumentSelection.collapsed(position: documentPosition); return true; } @@ -84,9 +83,11 @@ class CommonEditorOperations { }) { DocumentPosition? position; if (findNearestPosition) { - position = documentLayoutResolver().getDocumentPositionNearestToOffset(documentOffset); + position = documentLayoutResolver() + .getDocumentPositionNearestToOffset(documentOffset); } else { - position = documentLayoutResolver().getDocumentPositionAtOffset(documentOffset); + position = + documentLayoutResolver().getDocumentPositionAtOffset(documentOffset); } if (position != null) { @@ -139,7 +140,8 @@ class CommonEditorOperations { return false; } - final selectedNode = editor.document.getNodeById(composer.selection!.extent.nodeId); + final selectedNode = + editor.document.getNodeById(composer.selection!.extent.nodeId); if (selectedNode is! TextNode) { return false; } @@ -147,7 +149,8 @@ class CommonEditorOperations { final docSelection = composer.selection!; final currentSelection = TextSelection( baseOffset: (docSelection.base.nodePosition as TextNodePosition).offset, - extentOffset: (docSelection.extent.nodePosition as TextNodePosition).offset, + extentOffset: + (docSelection.extent.nodePosition as TextNodePosition).offset, ); final selectedText = currentSelection.textInside(selectedNode.text.text); @@ -158,9 +161,12 @@ class CommonEditorOperations { final wordTextSelection = expandPositionToWord( text: selectedNode.text.text, - textPosition: TextPosition(offset: (docSelection.extent.nodePosition as TextNodePosition).offset), + textPosition: TextPosition( + offset: + (docSelection.extent.nodePosition as TextNodePosition).offset), ); - final wordNodeSelection = TextNodeSelection.fromTextSelection(wordTextSelection); + final wordNodeSelection = + TextNodeSelection.fromTextSelection(wordTextSelection); composer.selection = DocumentSelection( base: DocumentPosition( @@ -192,7 +198,8 @@ class CommonEditorOperations { return false; } - final selectedNode = editor.document.getNodeById(composer.selection!.extent.nodeId); + final selectedNode = + editor.document.getNodeById(composer.selection!.extent.nodeId); if (selectedNode is! TextNode) { return false; } @@ -200,7 +207,8 @@ class CommonEditorOperations { final docSelection = composer.selection!; final currentSelection = TextSelection( baseOffset: (docSelection.base.nodePosition as TextNodePosition).offset, - extentOffset: (docSelection.extent.nodePosition as TextNodePosition).offset, + extentOffset: + (docSelection.extent.nodePosition as TextNodePosition).offset, ); final selectedText = currentSelection.textInside(selectedNode.text.text); @@ -211,9 +219,12 @@ class CommonEditorOperations { final paragraphTextSelection = expandPositionToParagraph( text: selectedNode.text.text, - textPosition: TextPosition(offset: (docSelection.extent.nodePosition as TextNodePosition).offset), + textPosition: TextPosition( + offset: + (docSelection.extent.nodePosition as TextNodePosition).offset), ); - final paragraphNodeSelection = TextNodeSelection.fromTextSelection(paragraphTextSelection); + final paragraphNodeSelection = + TextNodeSelection.fromTextSelection(paragraphTextSelection); composer.selection = DocumentSelection( base: DocumentPosition( @@ -291,7 +302,8 @@ class CommonEditorOperations { } if (!composer.selection!.isCollapsed && !expand) { - composer.selection = composer.selection!.collapseUpstream(editor.document); + composer.selection = + composer.selection!.collapseUpstream(editor.document); return true; } @@ -301,13 +313,15 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = + documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionLeft(currentExtent.nodePosition, movementModifiers); + dynamic newExtentNodePosition = extentComponent.movePositionLeft( + currentExtent.nodePosition, movementModifiers); if (newExtentNodePosition == null) { // Move to next node @@ -319,7 +333,8 @@ class CommonEditorOperations { } newExtentNodeId = nextNode.id; - final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = + documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } @@ -369,7 +384,8 @@ class CommonEditorOperations { } if (!composer.selection!.isCollapsed && !expand) { - composer.selection = composer.selection!.collapseDownstream(editor.document); + composer.selection = + composer.selection!.collapseDownstream(editor.document); return true; } @@ -379,13 +395,15 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = + documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionRight(currentExtent.nodePosition, movementModifiers); + dynamic newExtentNodePosition = extentComponent.movePositionRight( + currentExtent.nodePosition, movementModifiers); if (newExtentNodePosition == null) { // Move to next node @@ -398,7 +416,8 @@ class CommonEditorOperations { } newExtentNodeId = nextNode.id; - final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = + documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { throw Exception( 'Could not find next component to move the selection horizontally. Next node ID: ${nextNode.id}'); @@ -456,25 +475,30 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = + documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionUp(currentExtent.nodePosition); + dynamic newExtentNodePosition = + extentComponent.movePositionUp(currentExtent.nodePosition); if (newExtentNodePosition == null) { // Move to next node final nextNode = editor.document.getNodeBefore(node); if (nextNode != null) { newExtentNodeId = nextNode.id; - final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = + documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } - final offsetToMatch = extentComponent.getOffsetForPosition(currentExtent.nodePosition); - newExtentNodePosition = nextComponent.getEndPositionNearX(offsetToMatch.dx); + final offsetToMatch = + extentComponent.getOffsetForPosition(currentExtent.nodePosition); + newExtentNodePosition = + nextComponent.getEndPositionNearX(offsetToMatch.dx); } else { // We're at the top of the document. Move the cursor to the // beginning of the current node. @@ -532,25 +556,30 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = + documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionDown(currentExtent.nodePosition); + dynamic newExtentNodePosition = + extentComponent.movePositionDown(currentExtent.nodePosition); if (newExtentNodePosition == null) { // Move to next node final nextNode = editor.document.getNodeAfter(node); if (nextNode != null) { newExtentNodeId = nextNode.id; - final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = + documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } - final offsetToMatch = extentComponent.getOffsetForPosition(currentExtent.nodePosition); - newExtentNodePosition = nextComponent.getBeginningPositionNearX(offsetToMatch.dx); + final offsetToMatch = + extentComponent.getOffsetForPosition(currentExtent.nodePosition); + newExtentNodePosition = + nextComponent.getBeginningPositionNearX(offsetToMatch.dx); } else { // We're at the bottom of the document. Move the cursor to the // end of the current node. @@ -592,14 +621,20 @@ class CommonEditorOperations { return false; } - if (!composer.selection!.isCollapsed || composer.selection!.extent.nodePosition is BinaryNodePosition) { + if (!composer.selection!.isCollapsed || + composer.selection!.extent.nodePosition is BinaryNodePosition) { // A span of content is selected. Delete the selection. return deleteSelection(); } else if (composer.selection!.extent.nodePosition is TextNodePosition) { - final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; - final text = (editor.document.getNodeById(composer.selection!.extent.nodeId) as TextNode).text.text; + final textPosition = + composer.selection!.extent.nodePosition as TextNodePosition; + final text = (editor.document + .getNodeById(composer.selection!.extent.nodeId) as TextNode) + .text + .text; if (textPosition.offset == text.length) { - final node = editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final node = + editor.document.getNodeById(composer.selection!.extent.nodeId)!; final nodeAfter = editor.document.getNodeAfter(node); if (nodeAfter is TextNode) { @@ -687,21 +722,27 @@ class CommonEditorOperations { if (composer.selection == null) { return false; } - if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode( + document: editor.document, selection: composer.selection!)) { return false; } - if (composer.selection!.isCollapsed && (composer.selection!.extent.nodePosition as TextNodePosition).offset <= 0) { + if (composer.selection!.isCollapsed && + (composer.selection!.extent.nodePosition as TextNodePosition).offset <= + 0) { return false; } - final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; + final textNode = + editor.document.getNode(composer.selection!.extent) as TextNode; final text = textNode.text; - final currentTextPosition = (composer.selection!.extent.nodePosition as TextNodePosition); + final currentTextPosition = + (composer.selection!.extent.nodePosition as TextNodePosition); if (currentTextPosition.offset >= text.text.length) { return false; } - final nextCharacterOffset = getCharacterEndBounds(text.text, currentTextPosition.offset); + final nextCharacterOffset = + getCharacterEndBounds(text.text, currentTextPosition.offset); // Delete the selected content. editor.executeCommand( @@ -736,20 +777,25 @@ class CommonEditorOperations { return false; } - if (!composer.selection!.isCollapsed || composer.selection!.extent.nodePosition is BinaryNodePosition) { + if (!composer.selection!.isCollapsed || + composer.selection!.extent.nodePosition is BinaryNodePosition) { // A span of content is selected. Delete the selection. return deleteSelection(); } - final node = editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final node = + editor.document.getNodeById(composer.selection!.extent.nodeId)!; // If the caret is at the beginning of a list item, unindent the list item. - if (node is ListItemNode && (composer.selection!.extent.nodePosition as TextNodePosition).offset == 0) { + if (node is ListItemNode && + (composer.selection!.extent.nodePosition as TextNodePosition).offset == + 0) { return unindentListItem(); } if (composer.selection!.extent.nodePosition is TextNodePosition) { - final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = + composer.selection!.extent.nodePosition as TextNodePosition; if (textPosition.offset == 0) { final nodeBefore = editor.document.getNodeBefore(node); @@ -844,17 +890,23 @@ class CommonEditorOperations { if (composer.selection == null) { return false; } - if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode( + document: editor.document, selection: composer.selection!)) { return false; } - if (composer.selection!.isCollapsed && (composer.selection!.extent.nodePosition as TextNodePosition).offset <= 0) { + if (composer.selection!.isCollapsed && + (composer.selection!.extent.nodePosition as TextNodePosition).offset <= + 0) { return false; } - final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; - final currentTextPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final textNode = + editor.document.getNode(composer.selection!.extent) as TextNode; + final currentTextPosition = + composer.selection!.extent.nodePosition as TextNodePosition; - final previousCharacterOffset = getCharacterStartBounds(textNode.text.text, currentTextPosition.offset); + final previousCharacterOffset = + getCharacterStartBounds(textNode.text.text, currentTextPosition.offset); final newSelectionPosition = DocumentPosition( nodeId: textNode.id, @@ -877,7 +929,8 @@ class CommonEditorOperations { ), ); - composer.selection = DocumentSelection.collapsed(position: newSelectionPosition); + composer.selection = + DocumentSelection.collapsed(position: newSelectionPosition); return true; } @@ -895,7 +948,8 @@ class CommonEditorOperations { if (composer.selection!.extent.nodePosition is! BinaryNodePosition) { return false; } - if (!(composer.selection!.extent.nodePosition as BinaryNodePosition).isIncluded) { + if (!(composer.selection!.extent.nodePosition as BinaryNodePosition) + .isIncluded) { return false; } @@ -950,9 +1004,11 @@ class CommonEditorOperations { throw Exception( 'Tried to access document node at index $newSelectionNodeIndex but the document returned null.'); } - final component = documentLayout.getComponentByNodeId(newSelectionNode.id); + final component = + documentLayout.getComponentByNodeId(newSelectionNode.id); if (component == null) { - throw Exception('Couldn\'t find editor component for node: ${newSelectionNode.id}'); + throw Exception( + 'Couldn\'t find editor component for node: ${newSelectionNode.id}'); } return DocumentPosition( nodeId: newSelectionNode.id, @@ -964,11 +1020,14 @@ class CommonEditorOperations { // is now the first node in the document. final newSelectionNode = document.getNodeAt(0); if (newSelectionNode == null) { - throw Exception('Could not obtain the first node in a non-empty document.'); + throw Exception( + 'Could not obtain the first node in a non-empty document.'); } - final component = documentLayout.getComponentByNodeId(newSelectionNode.id); + final component = + documentLayout.getComponentByNodeId(newSelectionNode.id); if (component == null) { - throw Exception('Couldn\'t find editor component for node: ${newSelectionNode.id}'); + throw Exception( + 'Couldn\'t find editor component for node: ${newSelectionNode.id}'); } return DocumentPosition( nodeId: newSelectionNode.id, @@ -991,7 +1050,8 @@ class CommonEditorOperations { DeleteSelectionCommand(documentSelection: composer.selection!), ); - composer.selection = DocumentSelection.collapsed(position: newSelectionPosition); + composer.selection = + DocumentSelection.collapsed(position: newSelectionPosition); } DocumentPosition _getDocumentPositionAfterDeletion({ @@ -1006,14 +1066,16 @@ class CommonEditorOperations { final basePosition = selection.base; final baseNode = document.getNode(basePosition); if (baseNode == null) { - throw Exception('Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); + throw Exception( + 'Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); } final baseNodeIndex = document.getNodeIndex(baseNode); final extentPosition = selection.extent; final extentNode = document.getNode(extentPosition); if (extentNode == null) { - throw Exception('Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); + throw Exception( + 'Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); } final extentNodeIndex = document.getNodeIndex(extentNode); DocumentPosition newSelectionPosition; @@ -1021,7 +1083,8 @@ class CommonEditorOperations { if (baseNodeIndex != extentNodeIndex) { // Place the caret at the current position within the // first node in the selection. - newSelectionPosition = baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; + newSelectionPosition = + baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; // If it's a binary selection node then that node will // be replaced by a ParagraphNode with the same ID. @@ -1029,7 +1092,7 @@ class CommonEditorOperations { // Assume that the node was replaced with an empty paragraph. newSelectionPosition = DocumentPosition( nodeId: newSelectionPosition.nodeId, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ); } } else { @@ -1042,11 +1105,13 @@ class CommonEditorOperations { // Assume that the node was replace with an empty paragraph. newSelectionPosition = DocumentPosition( nodeId: baseNode.id, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ); } else if (basePosition.nodePosition is TextNodePosition) { - final baseOffset = (basePosition.nodePosition as TextNodePosition).offset; - final extentOffset = (extentPosition.nodePosition as TextNodePosition).offset; + final baseOffset = + (basePosition.nodePosition as TextNodePosition).offset; + final extentOffset = + (extentPosition.nodePosition as TextNodePosition).offset; newSelectionPosition = DocumentPosition( nodeId: baseNode.id, @@ -1184,8 +1249,10 @@ class CommonEditorOperations { return false; } - final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = + editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = + editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } @@ -1199,8 +1266,10 @@ class CommonEditorOperations { deleteSelection(); } - final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; - final initialTextOffset = (composer.selection!.extent.nodePosition as TextNodePosition).offset; + final textNode = + editor.document.getNode(composer.selection!.extent) as TextNode; + final initialTextOffset = + (composer.selection!.extent.nodePosition as TextNodePosition).offset; editor.executeCommand( InsertTextCommand( @@ -1270,20 +1339,26 @@ class CommonEditorOperations { } final text = node.text; - final textSelection = composer.selection!.extent.nodePosition as TextNodePosition; + final textSelection = + composer.selection!.extent.nodePosition as TextNodePosition; final textBeforeCaret = text.text.substring(0, textSelection.offset); final unorderedListItemMatch = RegExp(r'^\s*[\*-]\s+$'); - final hasUnorderedListItemMatch = unorderedListItemMatch.hasMatch(textBeforeCaret); + final hasUnorderedListItemMatch = + unorderedListItemMatch.hasMatch(textBeforeCaret); final orderedListItemMatch = RegExp(r'^\s*[1].*\s+$'); - final hasOrderedListItemMatch = orderedListItemMatch.hasMatch(textBeforeCaret); + final hasOrderedListItemMatch = + orderedListItemMatch.hasMatch(textBeforeCaret); - _log.log('_convertParagraphIfDesired', ' - text before caret: "$textBeforeCaret"'); + _log.log('_convertParagraphIfDesired', + ' - text before caret: "$textBeforeCaret"'); if (hasUnorderedListItemMatch || hasOrderedListItemMatch) { - _log.log('_convertParagraphIfDesired', ' - found unordered list item prefix'); + _log.log( + '_convertParagraphIfDesired', ' - found unordered list item prefix'); int startOfNewText = textBeforeCaret.length; - while (startOfNewText < node.text.text.length && node.text.text[startOfNewText] == ' ') { + while (startOfNewText < node.text.text.length && + node.text.text[startOfNewText] == ' ') { startOfNewText += 1; } final adjustedText = node.text.copyText(startOfNewText); @@ -1302,11 +1377,13 @@ class CommonEditorOperations { // We removed some text at the beginning of the list item. // Move the selection back by that same amount. - final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = + composer.selection!.extent.nodePosition as TextNodePosition; composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: node.id, - nodePosition: TextNodePosition(offset: textPosition.offset - startOfNewText), + nodePosition: + TextNodePosition(offset: textPosition.offset - startOfNewText), ), ); @@ -1332,12 +1409,13 @@ class CommonEditorOperations { }), ); - node.text = node.text.removeRegion(startOffset: 0, endOffset: hrMatch.firstMatch(textBeforeCaret)!.end); + node.text = node.text.removeRegion( + startOffset: 0, endOffset: hrMatch.firstMatch(textBeforeCaret)!.end); composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: node.id, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ), ); @@ -1348,7 +1426,8 @@ class CommonEditorOperations { final hasBlockquoteMatch = blockquoteMatch.hasMatch(textBeforeCaret); if (hasBlockquoteMatch) { int startOfNewText = textBeforeCaret.length; - while (startOfNewText < node.text.text.length && node.text.text[startOfNewText] == ' ') { + while (startOfNewText < node.text.text.length && + node.text.text[startOfNewText] == ' ') { startOfNewText += 1; } final adjustedText = node.text.copyText(startOfNewText); @@ -1369,11 +1448,13 @@ class CommonEditorOperations { // We removed some text at the beginning of the list item. // Move the selection back by that same amount. - final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = + composer.selection!.extent.nodePosition as TextNodePosition; composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: node.id, - nodePosition: TextNodePosition(offset: textPosition.offset - startOfNewText), + nodePosition: + TextNodePosition(offset: textPosition.offset - startOfNewText), ), ); @@ -1383,16 +1464,21 @@ class CommonEditorOperations { // URL match, e.g., images, social, etc. _log.log('_convertParagraphIfDesired', 'Looking for URL match...'); final extractedLinks = linkify(node.text.text, - options: LinkifyOptions( + options: const LinkifyOptions( humanize: false, )); - final int linkCount = extractedLinks.fold(0, (value, element) => element is UrlElement ? value + 1 : value); - final String nonEmptyText = - extractedLinks.fold('', (value, element) => element is TextElement ? value + element.text.trim() : value); + final int linkCount = extractedLinks.fold( + 0, (value, element) => element is UrlElement ? value + 1 : value); + final String nonEmptyText = extractedLinks.fold( + '', + (value, element) => + element is TextElement ? value + element.text.trim() : value); if (linkCount == 1 && nonEmptyText.isEmpty) { // This node's text is just a URL, try to interpret it // as a known type. - final link = extractedLinks.firstWhereOrNull((element) => element is UrlElement)!.text; + final link = extractedLinks + .firstWhereOrNull((element) => element is UrlElement)! + .text; _processUrlNode( document: editor.document, editor: editor, @@ -1417,7 +1503,8 @@ class CommonEditorOperations { final response = await http.get(Uri.parse(url)); if (response.statusCode < 200 || response.statusCode >= 300) { - _log.log('_processUrlNode', 'Failed to load URL: ${response.statusCode} - ${response.reasonPhrase}'); + _log.log('_processUrlNode', + 'Failed to load URL: ${response.statusCode} - ${response.reasonPhrase}'); return; } @@ -1432,16 +1519,18 @@ class CommonEditorOperations { } // The URL is an image. Convert the node. - _log.log('_processUrlNode', 'The URL is an image. Converting the ParagraphNode to an ImageNode.'); + _log.log('_processUrlNode', + 'The URL is an image. Converting the ParagraphNode to an ImageNode.'); final node = document.getNodeById(nodeId); if (node is! ParagraphNode) { - _log.log( - '_processUrlNode', 'The node has become something other than a ParagraphNode ($node). Can\'t convert ndoe.'); + _log.log('_processUrlNode', + 'The node has become something other than a ParagraphNode ($node). Can\'t convert ndoe.'); return; } final currentText = node.text.text; if (currentText.trim() != originalText.trim()) { - _log.log('_processUrlNode', 'The node content changed in a non-trivial way. Aborting node conversion.'); + _log.log('_processUrlNode', + 'The node content changed in a non-trivial way. Aborting node conversion.'); return; } @@ -1477,18 +1566,23 @@ class CommonEditorOperations { if (!composer.selection!.isCollapsed) { return false; } - if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode( + document: editor.document, selection: composer.selection!)) { return false; } - final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; - final initialTextOffset = (composer.selection!.extent.nodePosition as TextNodePosition).offset; + final textNode = + editor.document.getNode(composer.selection!.extent) as TextNode; + final initialTextOffset = + (composer.selection!.extent.nodePosition as TextNodePosition).offset; editor.executeCommand( InsertTextCommand( documentPosition: composer.selection!.extent, textToInsert: character, - attributions: ignoreComposerAttributions ? {} : composer.preferences.currentAttributions, + attributions: ignoreComposerAttributions + ? {} + : composer.preferences.currentAttributions, ), ); @@ -1527,8 +1621,10 @@ class CommonEditorOperations { } // Ensure that the entire selection sits within the same node. - final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = + editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = + editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } @@ -1551,14 +1647,16 @@ class CommonEditorOperations { editor.executeCommand( SplitListItemCommand( nodeId: extentNode.id, - splitPosition: composer.selection!.extent.nodePosition as TextNodePosition, + splitPosition: + composer.selection!.extent.nodePosition as TextNodePosition, newNodeId: newNodeId, ), ); } else if (extentNode is ParagraphNode) { // Split the paragraph into two. This includes headers, blockquotes, and // any other block-level paragraph. - final currentExtentPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final currentExtentPosition = + composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = extentNode.endPosition; editor.executeCommand( @@ -1566,7 +1664,8 @@ class CommonEditorOperations { nodeId: extentNode.id, splitPosition: currentExtentPosition, newNodeId: newNodeId, - replicateExistingMetdata: currentExtentPosition.offset != endOfParagraph.offset, + replicateExistingMetdata: + currentExtentPosition.offset != endOfParagraph.offset, ), ); } else { @@ -1587,7 +1686,7 @@ class CommonEditorOperations { composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: newNodeId, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ), ); @@ -1628,12 +1727,13 @@ class CommonEditorOperations { editor.executeCommand( EditorCommandFunction((document, transaction) { - final paragraphPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final paragraphPosition = + composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = node.endPosition; final nodeIndex = editor.document.getNodeIndex(node); - var newSelection; + DocumentSelection newSelection; if (node.text.text.isEmpty) { // Convert empty paragraph to HR. final imageNode = ImageNode(id: nodeId, imageUrl: url); @@ -1650,7 +1750,8 @@ class CommonEditorOperations { ); } else if (paragraphPosition == endOfParagraph) { // Insert HR after the paragraph. - final imageNode = ImageNode(id: DocumentEditor.createNodeId(), imageUrl: url); + final imageNode = + ImageNode(id: DocumentEditor.createNodeId(), imageUrl: url); transaction.insertNodeAfter(previousNode: node, newNode: imageNode); @@ -1666,7 +1767,8 @@ class CommonEditorOperations { final textAfter = node.text.copyText(paragraphPosition.offset); final imageNode = ImageNode(id: nodeId, imageUrl: url); - final newParagraph = ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); + final newParagraph = + ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); // TODO: node operations need to be a part of a transaction, somehow. node.text = textBefore; @@ -1725,10 +1827,11 @@ class CommonEditorOperations { editor.executeCommand( EditorCommandFunction((document, transaction) { - final paragraphPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final paragraphPosition = + composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = node.endPosition; - var newSelection; + DocumentSelection newSelection; if (node.text.text.isEmpty) { // Convert empty paragraph to HR. final hrNode = HorizontalRuleNode(id: nodeId); @@ -1761,7 +1864,8 @@ class CommonEditorOperations { final textAfter = node.text.copyText(paragraphPosition.offset); final hrNode = HorizontalRuleNode(id: DocumentEditor.createNodeId()); - final newParagraph = ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); + final newParagraph = + ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); // TODO: node operations need to be a part of a transaction, somehow. node.text = textBefore; @@ -1795,8 +1899,10 @@ class CommonEditorOperations { return false; } - final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId); - final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId); + final baseNode = + editor.document.getNodeById(composer.selection!.base.nodeId); + final extentNode = + editor.document.getNodeById(composer.selection!.extent.nodeId); if (baseNode is! ListItemNode || extentNode is! ListItemNode) { return false; } @@ -1822,8 +1928,10 @@ class CommonEditorOperations { return false; } - final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId); - final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId); + final baseNode = + editor.document.getNodeById(composer.selection!.base.nodeId); + final extentNode = + editor.document.getNodeById(composer.selection!.extent.nodeId); if (baseNode!.id != extentNode!.id) { return false; } @@ -1899,7 +2007,8 @@ class CommonEditorOperations { } final nodeIndex = editor.document.getNodeIndex(node); - final newNode = ParagraphNode(id: nodeId, metadata: {'blockType': blockquoteAttribution}, text: text); + final newNode = ParagraphNode( + id: nodeId, metadata: {'blockType': blockquoteAttribution}, text: text); editor.executeCommand( EditorCommandFunction((document, transaction) { @@ -1930,15 +2039,18 @@ class CommonEditorOperations { return false; } - final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = + editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = + editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } if (extentNode is! TextNode) { return false; } - if (extentNode is ParagraphNode && !extentNode.metadata.containsKey('blockType')) { + if (extentNode is ParagraphNode && + !extentNode.metadata.containsKey('blockType')) { // This content is already a regular paragraph. return false; } @@ -1948,6 +2060,7 @@ class CommonEditorOperations { if (extentNode is ParagraphNode) { extentNode.metadata.remove('blockType'); // TODO: find a way to alter nodes that automatically notifies listeners + // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member extentNode.notifyListeners(); } else { final newParagraphNode = ParagraphNode( diff --git a/super_editor/lib/src/default_editor/document_interaction.dart b/super_editor/lib/src/default_editor/document_interaction.dart index 80b94d275c..26a7f9fc23 100644 --- a/super_editor/lib/src/default_editor/document_interaction.dart +++ b/super_editor/lib/src/default_editor/document_interaction.dart @@ -60,13 +60,14 @@ class DocumentInteractor extends StatefulWidget { /// Paints some extra visual ornamentation to help with /// debugging, when true. - final showDebugPaint; + final bool showDebugPaint; @override _DocumentInteractorState createState() => _DocumentInteractorState(); } -class _DocumentInteractorState extends State with SingleTickerProviderStateMixin { +class _DocumentInteractorState extends State + with SingleTickerProviderStateMixin { final _dragGutterExtent = 100; final _maxDragSpeed = 20; @@ -97,7 +98,8 @@ class _DocumentInteractorState extends State with SingleTick _focusNode = widget.focusNode ?? FocusNode(); _ticker = createTicker(_onTick); _scrollController = - _scrollController = (widget.scrollController ?? ScrollController())..addListener(_updateDragSelection); + _scrollController = (widget.scrollController ?? ScrollController()) + ..addListener(_updateDragSelection); widget.editContext.composer.addListener(_onSelectionChange); } @@ -114,7 +116,8 @@ class _DocumentInteractorState extends State with SingleTick if (oldWidget.scrollController == null) { _scrollController.dispose(); } - _scrollController = (widget.scrollController ?? ScrollController())..addListener(_updateDragSelection); + _scrollController = (widget.scrollController ?? ScrollController()) + ..addListener(_updateDragSelection); } if (widget.focusNode != oldWidget.focusNode) { _focusNode = widget.focusNode ?? FocusNode(); @@ -150,7 +153,8 @@ class _DocumentInteractorState extends State with SingleTick } void _ensureSelectionExtentIsVisible() { - _log.log('_ensureSelectionExtentIsVisible', 'selection: ${widget.editContext.composer.selection}'); + _log.log('_ensureSelectionExtentIsVisible', + 'selection: ${widget.editContext.composer.selection}'); final selection = widget.editContext.composer.selection; if (selection == null) { return; @@ -170,20 +174,30 @@ class _DocumentInteractorState extends State with SingleTick } final myBox = context.findRenderObject() as RenderBox; - final beyondTopExtent = min(extentRect.top - _scrollController.offset - _dragGutterExtent, 0).abs(); - final beyondBottomExtent = - max(extentRect.bottom - myBox.size.height - _scrollController.offset + _dragGutterExtent, 0); + final beyondTopExtent = + min(extentRect.top - _scrollController.offset - _dragGutterExtent, 0) + .abs(); + final beyondBottomExtent = max( + extentRect.bottom - + myBox.size.height - + _scrollController.offset + + _dragGutterExtent, + 0); _log.log('_ensureSelectionExtentIsVisible', 'Ensuring extent is visible.'); - _log.log('_ensureSelectionExtentIsVisible', ' - interaction size: ${myBox.size}'); - _log.log('_ensureSelectionExtentIsVisible', ' - scroll extent: ${_scrollController.offset}'); + _log.log('_ensureSelectionExtentIsVisible', + ' - interaction size: ${myBox.size}'); + _log.log('_ensureSelectionExtentIsVisible', + ' - scroll extent: ${_scrollController.offset}'); _log.log('_ensureSelectionExtentIsVisible', ' - extent rect: $extentRect'); - _log.log('_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); - _log.log('_ensureSelectionExtentIsVisible', ' - beyond bottom: $beyondBottomExtent'); + _log.log( + '_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); + _log.log('_ensureSelectionExtentIsVisible', + ' - beyond bottom: $beyondBottomExtent'); if (beyondTopExtent > 0) { - final newScrollPosition = - (_scrollController.offset - beyondTopExtent).clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollPosition = (_scrollController.offset - beyondTopExtent) + .clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.animateTo( newScrollPosition, @@ -191,8 +205,8 @@ class _DocumentInteractorState extends State with SingleTick curve: Curves.easeOut, ); } else if (beyondBottomExtent > 0) { - final newScrollPosition = - (beyondBottomExtent + _scrollController.offset).clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollPosition = (beyondBottomExtent + _scrollController.offset) + .clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.animateTo( newScrollPosition, @@ -211,7 +225,8 @@ class _DocumentInteractorState extends State with SingleTick ExecutionInstruction instruction = ExecutionInstruction.continueExecution; int index = 0; - while (instruction == ExecutionInstruction.continueExecution && index < widget.keyboardActions.length) { + while (instruction == ExecutionInstruction.continueExecution && + index < widget.keyboardActions.length) { instruction = widget.keyboardActions[index]( editContext: widget.editContext, keyEvent: keyEvent, @@ -219,7 +234,9 @@ class _DocumentInteractorState extends State with SingleTick index += 1; } - return instruction == ExecutionInstruction.haltExecution ? KeyEventResult.handled : KeyEventResult.ignored; + return instruction == ExecutionInstruction.haltExecution + ? KeyEventResult.handled + : KeyEventResult.ignored; } void _onTapDown(TapDownDetails details) { @@ -305,7 +322,8 @@ class _DocumentInteractorState extends State with SingleTick _dragStartInDoc = _getDocOffset(_dragStartInViewport!); _clearSelection(); - _dragRectInViewport = Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); + _dragRectInViewport = + Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); _focusNode.requestFocus(); } @@ -315,7 +333,8 @@ class _DocumentInteractorState extends State with SingleTick setState(() { _dragEndInViewport = details.localPosition; _dragEndInDoc = _getDocOffset(_dragEndInViewport!); - _dragRectInViewport = Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); + _dragRectInViewport = + Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); _log.log('_onPanUpdate', ' - drag rect: $_dragRectInViewport'); _updateCursorStyle(details.localPosition); _updateDragSelection(); @@ -354,7 +373,8 @@ class _DocumentInteractorState extends State with SingleTick required DocumentPosition docPosition, required DocumentLayout docLayout, }) { - final newSelection = getWordSelection(docPosition: docPosition, docLayout: docLayout); + final newSelection = + getWordSelection(docPosition: docPosition, docLayout: docLayout); if (newSelection != null) { widget.editContext.composer.selection = newSelection; return true; @@ -367,7 +387,8 @@ class _DocumentInteractorState extends State with SingleTick required DocumentPosition docPosition, required DocumentLayout docLayout, }) { - final newSelection = getParagraphSelection(docPosition: docPosition, docLayout: docLayout); + final newSelection = + getParagraphSelection(docPosition: docPosition, docLayout: docLayout); if (newSelection != null) { widget.editContext.composer.selection = newSelection; return true; @@ -404,11 +425,14 @@ class _DocumentInteractorState extends State with SingleTick required Offset extentOffset, required SelectionType selectionType, }) { - _log.log('_selectionRegion', 'Composer: selectionRegion(). Mode: $selectionType'); - DocumentSelection? selection = documentLayout.getDocumentSelectionInRegion(baseOffset, extentOffset); + _log.log('_selectionRegion', + 'Composer: selectionRegion(). Mode: $selectionType'); + DocumentSelection? selection = + documentLayout.getDocumentSelectionInRegion(baseOffset, extentOffset); DocumentPosition? basePosition = selection?.base; DocumentPosition? extentPosition = selection?.extent; - _log.log('_selectionRegion', ' - base: $basePosition, extent: $extentPosition'); + _log.log( + '_selectionRegion', ' - base: $basePosition, extent: $extentPosition'); if (basePosition == null || extentPosition == null) { widget.editContext.composer.selection = null; @@ -424,7 +448,9 @@ class _DocumentInteractorState extends State with SingleTick widget.editContext.composer.selection = null; return; } - basePosition = baseOffset.dy < extentOffset.dy ? baseParagraphSelection.base : baseParagraphSelection.extent; + basePosition = baseOffset.dy < extentOffset.dy + ? baseParagraphSelection.base + : baseParagraphSelection.extent; final extentParagraphSelection = getParagraphSelection( docPosition: extentPosition, @@ -434,8 +460,9 @@ class _DocumentInteractorState extends State with SingleTick widget.editContext.composer.selection = null; return; } - extentPosition = - baseOffset.dy < extentOffset.dy ? extentParagraphSelection.extent : extentParagraphSelection.base; + extentPosition = baseOffset.dy < extentOffset.dy + ? extentParagraphSelection.extent + : extentParagraphSelection.base; } else if (selectionType == SelectionType.word) { _log.log('_selectionRegion', ' - selecting a word'); final baseWordSelection = getWordSelection( @@ -463,7 +490,8 @@ class _DocumentInteractorState extends State with SingleTick base: basePosition, extent: extentPosition, )); - _log.log('_selectionRegion', 'Region selection: ${widget.editContext.composer.selection}'); + _log.log('_selectionRegion', + 'Region selection: ${widget.editContext.composer.selection}'); } void _clearSelection() { @@ -476,7 +504,8 @@ class _DocumentInteractorState extends State with SingleTick if (desiredCursor != null && desiredCursor != _cursorStyle.value) { _cursorStyle.value = desiredCursor; - } else if (desiredCursor == null && _cursorStyle.value != SystemMouseCursors.basic) { + } else if (desiredCursor == null && + _cursorStyle.value != SystemMouseCursors.basic) { _cursorStyle.value = SystemMouseCursors.basic; } } @@ -484,7 +513,8 @@ class _DocumentInteractorState extends State with SingleTick // Converts the given [offset] from the [DocumentInteractor]'s coordinate // space to the [DocumentLayout]'s coordinate space. Offset _getDocOffset(Offset offset) { - return _layout.getDocumentOffsetFromAncestorOffset(offset, context.findRenderObject()!); + return _layout.getDocumentOffsetFromAncestorOffset( + offset, context.findRenderObject()!); } // ------ scrolling ------- @@ -494,8 +524,8 @@ class _DocumentInteractorState extends State with SingleTick /// form of mouse scrolling. void _onPointerSignal(PointerSignalEvent event) { if (event is PointerScrollEvent) { - final newScrollOffset = - (_scrollController.offset + event.scrollDelta.dy).clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollOffset = (_scrollController.offset + event.scrollDelta.dy) + .clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.jumpTo(newScrollOffset); _updateDragSelection(); @@ -506,7 +536,8 @@ class _DocumentInteractorState extends State with SingleTick // - _dragEndInViewport must be non-null void _scrollIfNearBoundary() { if (_dragEndInViewport == null) { - _log.log('_scrollIfNearBoundary', "Can't scroll near boundary because _dragEndInViewport is null"); + _log.log('_scrollIfNearBoundary', + "Can't scroll near boundary because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -545,7 +576,8 @@ class _DocumentInteractorState extends State with SingleTick void _scrollUp() { if (_dragEndInViewport == null) { - _log.log('_scrollUp', "Can't scroll up because _dragEndInViewport is null"); + _log.log( + '_scrollUp', "Can't scroll up because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -581,17 +613,20 @@ class _DocumentInteractorState extends State with SingleTick void _scrollDown() { if (_dragEndInViewport == null) { - _log.log('_scrollDown', "Can't scroll down because _dragEndInViewport is null"); + _log.log('_scrollDown', + "Can't scroll down because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } - if (_scrollController.offset >= _scrollController.position.maxScrollExtent) { + if (_scrollController.offset >= + _scrollController.position.maxScrollExtent) { return; } final editorBox = context.findRenderObject() as RenderBox; - final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy).clamp(0.0, _dragGutterExtent); + final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy) + .clamp(0.0, _dragGutterExtent); final speedPercent = 1.0 - (gutterAmount / _dragGutterExtent); final scrollAmount = lerpDouble(0, _maxDragSpeed, speedPercent); @@ -619,7 +654,9 @@ class _DocumentInteractorState extends State with SingleTick document: widget.document, ), Positioned.fill( - child: widget.showDebugPaint ? _buildDragSelection() : SizedBox(), + child: widget.showDebugPaint + ? _buildDragSelection() + : const SizedBox(), ), ], ), @@ -670,7 +707,8 @@ class _DocumentInteractorState extends State with SingleTick child: RawGestureDetector( behavior: HitTestBehavior.translucent, gestures: { - TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers( + TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers< + TapSequenceGestureRecognizer>( () => TapSequenceGestureRecognizer(), (TapSequenceGestureRecognizer recognizer) { recognizer @@ -681,7 +719,8 @@ class _DocumentInteractorState extends State with SingleTick ..onTripleTap = _onTripleTap; }, ), - PanGestureRecognizer: GestureRecognizerFactoryWithHandlers( + PanGestureRecognizer: + GestureRecognizerFactoryWithHandlers( () => PanGestureRecognizer(), (PanGestureRecognizer recognizer) { recognizer @@ -703,7 +742,7 @@ class _DocumentInteractorState extends State with SingleTick }) { return SingleChildScrollView( controller: _scrollController, - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), child: Center( child: SizedBox( key: _documentWrapperKey, diff --git a/super_editor/lib/src/default_editor/document_keyboard_actions.dart b/super_editor/lib/src/default_editor/document_keyboard_actions.dart index 60fd21564f..6bd5fd0ba1 100644 --- a/super_editor/lib/src/default_editor/document_keyboard_actions.dart +++ b/super_editor/lib/src/default_editor/document_keyboard_actions.dart @@ -33,7 +33,8 @@ ExecutionInstruction pasteWhenCmdVIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'v') { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.character?.toLowerCase() != 'v') { return ExecutionInstruction.continueExecution; } if (editContext.composer.selection == null) { @@ -52,10 +53,12 @@ ExecutionInstruction pasteWhenCmdVIsPressed({ // Delete the selected content. editContext.editor.executeCommand( - DeleteSelectionCommand(documentSelection: editContext.composer.selection!), + DeleteSelectionCommand( + documentSelection: editContext.composer.selection!), ); - editContext.composer.selection = DocumentSelection.collapsed(position: pastePosition); + editContext.composer.selection = + DocumentSelection.collapsed(position: pastePosition); } // TODO: figure out a general approach for asynchronous behaviors that @@ -74,12 +77,15 @@ ExecutionInstruction selectAllWhenCmdAIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'a') { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.character?.toLowerCase() != 'a') { return ExecutionInstruction.continueExecution; } final didSelectAll = editContext.commonOps.selectAll(); - return didSelectAll ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didSelectAll + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } Future _paste({ @@ -121,16 +127,20 @@ class _PasteEditorCommand implements EditorCommand { _log.log('_PasteEditorCommand', ' - "$piece"'); } - final currentNodeWithSelection = document.getNodeById(_pastePosition.nodeId); + final currentNodeWithSelection = + document.getNodeById(_pastePosition.nodeId); DocumentPosition? newSelectionPosition; if (currentNodeWithSelection is TextNode) { final textNode = document.getNode(_pastePosition) as TextNode; - final pasteTextOffset = (_pastePosition.nodePosition as TextPosition).offset; - final attributionsAtPasteOffset = textNode.text.getAllAttributionsAt(pasteTextOffset); + final pasteTextOffset = + (_pastePosition.nodePosition as TextPosition).offset; + final attributionsAtPasteOffset = + textNode.text.getAllAttributionsAt(pasteTextOffset); - if (splitContent.length > 1 && pasteTextOffset < textNode.endPosition.offset) { + if (splitContent.length > 1 && + pasteTextOffset < textNode.endPosition.offset) { // There is more than 1 node of content being pasted. Therefore, // new nodes will need to be added, which means that the currently // selected text node will be split at the current text offset. @@ -145,7 +155,8 @@ class _PasteEditorCommand implements EditorCommand { replicateExistingMetdata: false, ).execute(document, transaction); } else { - throw Exception('Can\'t handle pasting text within node of type: $currentNodeWithSelection'); + throw Exception( + 'Can\'t handle pasting text within node of type: $currentNodeWithSelection'); } } @@ -219,7 +230,8 @@ ExecutionInstruction copyWhenCmdVIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'c') { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.character?.toLowerCase() != 'c') { return ExecutionInstruction.continueExecution; } if (editContext.composer.selection == null) { @@ -256,12 +268,14 @@ Future _copy({ if (i == 0) { // This is the first node and it may be partially selected. - final baseSelectionPosition = selectedNode.id == documentSelection.base.nodeId - ? documentSelection.base.nodePosition - : documentSelection.extent.nodePosition; + final baseSelectionPosition = + selectedNode.id == documentSelection.base.nodeId + ? documentSelection.base.nodePosition + : documentSelection.extent.nodePosition; - final extentSelectionPosition = - selectedNodes.length > 1 ? selectedNode.endPosition : documentSelection.extent.nodePosition; + final extentSelectionPosition = selectedNodes.length > 1 + ? selectedNode.endPosition + : documentSelection.extent.nodePosition; nodeSelection = selectedNode.computeSelection( base: baseSelectionPosition, @@ -305,7 +319,8 @@ ExecutionInstruction cmdBToToggleBold({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'b') { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.character?.toLowerCase() != 'b') { return ExecutionInstruction.continueExecution; } @@ -322,7 +337,8 @@ ExecutionInstruction cmdIToToggleItalics({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'i') { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.character?.toLowerCase() != 'i') { return ExecutionInstruction.continueExecution; } @@ -339,8 +355,10 @@ ExecutionInstruction anyCharacterOrDestructiveKeyToDeleteSelection({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - _log.log('deleteExpandedSelectionWhenCharacterOrDestructiveKeyPressed', 'Running...'); - if (editContext.composer.selection == null || editContext.composer.selection!.isCollapsed) { + _log.log('deleteExpandedSelectionWhenCharacterOrDestructiveKeyPressed', + 'Running...'); + if (editContext.composer.selection == null || + editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } @@ -351,7 +369,8 @@ ExecutionInstruction anyCharacterOrDestructiveKeyToDeleteSelection({ final isShiftPressed = keyEvent.isShiftPressed; final isDestructiveKey = - keyEvent.logicalKey == LogicalKeyboardKey.backspace || keyEvent.logicalKey == LogicalKeyboardKey.delete; + keyEvent.logicalKey == LogicalKeyboardKey.backspace || + keyEvent.logicalKey == LogicalKeyboardKey.delete; final shouldDeleteSelection = !isShiftPressed && (isDestructiveKey || @@ -378,14 +397,16 @@ DocumentPosition _getDocumentPositionAfterDeletion({ final basePosition = selection.base; final baseNode = document.getNode(basePosition); if (baseNode == null) { - throw Exception('Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); + throw Exception( + 'Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); } final baseNodeIndex = document.getNodeIndex(baseNode); final extentPosition = selection.extent; final extentNode = document.getNode(extentPosition); if (extentNode == null) { - throw Exception('Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); + throw Exception( + 'Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); } final extentNodeIndex = document.getNodeIndex(extentNode); DocumentPosition newSelectionPosition; @@ -393,7 +414,8 @@ DocumentPosition _getDocumentPositionAfterDeletion({ if (baseNodeIndex != extentNodeIndex) { // Place the caret at the current position within the // first node in the selection. - newSelectionPosition = baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; + newSelectionPosition = + baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; // If it's a binary selection node then that node will // be replaced by a ParagraphNode with the same ID. @@ -401,7 +423,7 @@ DocumentPosition _getDocumentPositionAfterDeletion({ // Assume that the node was replaced with an empty paragraph. newSelectionPosition = DocumentPosition( nodeId: newSelectionPosition.nodeId, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ); } } else { @@ -414,7 +436,7 @@ DocumentPosition _getDocumentPositionAfterDeletion({ // Assume that the node was replace with an empty paragraph. newSelectionPosition = DocumentPosition( nodeId: baseNode.id, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), ); } else if (basePosition.nodePosition is TextPosition) { final baseOffset = (basePosition.nodePosition as TextPosition).offset; @@ -443,7 +465,9 @@ ExecutionInstruction backspaceToRemoveUpstreamContent({ final didDelete = editContext.commonOps.deleteUpstream(); - return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didDelete + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction mergeNodeWithNextWhenDeleteIsPressed({ @@ -458,19 +482,23 @@ ExecutionInstruction mergeNodeWithNextWhenDeleteIsPressed({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document + .getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! TextNode) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'WARNING: Cannot combine node of type: $node'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', + 'WARNING: Cannot combine node of type: $node'); return ExecutionInstruction.continueExecution; } final nextNode = editContext.editor.document.getNodeAfter(node); if (nextNode == null) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'At bottom of document. Cannot merge with node above.'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', + 'At bottom of document. Cannot merge with node above.'); return ExecutionInstruction.continueExecution; } if (nextNode is! TextNode) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'Cannot merge ParagraphNode into node of type: $nextNode'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', + 'Cannot merge ParagraphNode into node of type: $nextNode'); return ExecutionInstruction.continueExecution; } @@ -511,8 +539,10 @@ ExecutionInstruction moveUpDownLeftAndRightWithArrowKeys({ } bool didMove = false; - if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft || keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); + if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft || + keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); final movementModifiers = {}; if (keyEvent.isPrimaryShortcutKeyPressed) { @@ -537,12 +567,17 @@ ExecutionInstruction moveUpDownLeftAndRightWithArrowKeys({ } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowUp) { _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); - didMove = editContext.commonOps.moveCaretUp(expand: keyEvent.isShiftPressed); + didMove = + editContext.commonOps.moveCaretUp(expand: keyEvent.isShiftPressed); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowDown) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); - didMove = editContext.commonOps.moveCaretDown(expand: keyEvent.isShiftPressed); + didMove = + editContext.commonOps.moveCaretDown(expand: keyEvent.isShiftPressed); } - return didMove ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didMove + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } diff --git a/super_editor/lib/src/default_editor/horizontal_rule.dart b/super_editor/lib/src/default_editor/horizontal_rule.dart index af1e03a855..51d3e044fa 100644 --- a/super_editor/lib/src/default_editor/horizontal_rule.dart +++ b/super_editor/lib/src/default_editor/horizontal_rule.dart @@ -18,18 +18,22 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { final String id; @override - BinaryNodePosition get beginningPosition => BinaryNodePosition.included(); + BinaryNodePosition get beginningPosition => + const BinaryNodePosition.included(); @override - BinaryNodePosition get endPosition => BinaryNodePosition.included(); + BinaryNodePosition get endPosition => const BinaryNodePosition.included(); @override - NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -38,12 +42,15 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { } @override - NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -56,16 +63,19 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { @required dynamic base, @required dynamic extent, }) { - return BinarySelection.all(); + return const BinarySelection.all(); } @override String? copyContent(dynamic selection) { if (selection is! BinarySelection) { - throw Exception('HorizontalRuleNode can only copy content from a BinarySelection.'); + throw Exception( + 'HorizontalRuleNode can only copy content from a BinarySelection.'); } - return selection.position == BinaryNodePosition.included() ? '---' : null; + return selection.position == const BinaryNodePosition.included() + ? '---' + : null; } @override @@ -118,13 +128,16 @@ Widget? horizontalRuleBuilder(ComponentContext componentContext) { return null; } - final selection = - componentContext.nodeSelection == null ? null : componentContext.nodeSelection!.nodeSelection as BinarySelection; + final selection = componentContext.nodeSelection == null + ? null + : componentContext.nodeSelection!.nodeSelection as BinarySelection; final isSelected = selection != null && selection.position.isIncluded; return HorizontalRuleComponent( componentKey: componentContext.componentKey, isSelected: isSelected, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .selectionColor, ); } diff --git a/super_editor/lib/src/default_editor/image.dart b/super_editor/lib/src/default_editor/image.dart index fba183a4c1..f84afcc0bc 100644 --- a/super_editor/lib/src/default_editor/image.dart +++ b/super_editor/lib/src/default_editor/image.dart @@ -38,18 +38,22 @@ class ImageNode with ChangeNotifier implements DocumentNode { } @override - BinaryNodePosition get beginningPosition => BinaryNodePosition.included(); + BinaryNodePosition get beginningPosition => + const BinaryNodePosition.included(); @override - BinaryNodePosition get endPosition => BinaryNodePosition.included(); + BinaryNodePosition get endPosition => const BinaryNodePosition.included(); @override - NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -58,12 +62,15 @@ class ImageNode with ChangeNotifier implements DocumentNode { } @override - NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -76,21 +83,26 @@ class ImageNode with ChangeNotifier implements DocumentNode { @required dynamic base, @required dynamic extent, }) { - return BinarySelection.all(); + return const BinarySelection.all(); } @override String? copyContent(dynamic selection) { if (selection is! BinarySelection) { - throw Exception('ImageNode can only copy content from a BinarySelection.'); + throw Exception( + 'ImageNode can only copy content from a BinarySelection.'); } - return selection.position == BinaryNodePosition.included() ? _imageUrl : null; + return selection.position == const BinaryNodePosition.included() + ? _imageUrl + : null; } @override bool hasEquivalentContent(DocumentNode other) { - return other is ImageNode && imageUrl == other.imageUrl && altText == other.altText; + return other is ImageNode && + imageUrl == other.imageUrl && + altText == other.altText; } } @@ -138,14 +150,17 @@ Widget? imageBuilder(ComponentContext componentContext) { return null; } - final selection = - componentContext.nodeSelection == null ? null : componentContext.nodeSelection!.nodeSelection as BinarySelection; + final selection = componentContext.nodeSelection == null + ? null + : componentContext.nodeSelection!.nodeSelection as BinarySelection; final isSelected = selection != null && selection.position.isIncluded; return ImageComponent( componentKey: componentContext.componentKey, imageUrl: (componentContext.documentNode as ImageNode).imageUrl, isSelected: isSelected, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .selectionColor, ); } diff --git a/super_editor/lib/src/default_editor/list_items.dart b/super_editor/lib/src/default_editor/list_items.dart index ca5f83c227..92e259b129 100644 --- a/super_editor/lib/src/default_editor/list_items.dart +++ b/super_editor/lib/src/default_editor/list_items.dart @@ -72,7 +72,10 @@ class ListItemNode extends TextNode { @override bool hasEquivalentContent(DocumentNode other) { - return other is ListItemNode && type == other.type && indent == other.indent && text == other.text; + return other is ListItemNode && + type == other.type && + indent == other.indent && + text == other.text; } } @@ -116,15 +119,17 @@ class UnorderedListItemComponent extends StatelessWidget { Widget build(BuildContext context) { final indentSpace = indentExtent * (indent + 1); final firstLineHeight = styleBuilder({}).fontSize; - final manualVerticalAdjustment = 2.0; + const manualVerticalAdjustment = 2.0; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - margin: EdgeInsets.only(top: manualVerticalAdjustment), + margin: const EdgeInsets.only(top: manualVerticalAdjustment), decoration: BoxDecoration( - border: Border.all(width: 1, color: showDebugPaint ? Colors.grey : Colors.transparent), + border: Border.all( + width: 1, + color: showDebugPaint ? Colors.grey : Colors.transparent), ), child: SizedBox( width: indentSpace, @@ -149,9 +154,11 @@ class UnorderedListItemComponent extends StatelessWidget { } } -typedef UnorderedListItemDotBuilder = Widget Function(BuildContext, UnorderedListItemComponent); +typedef UnorderedListItemDotBuilder = Widget Function( + BuildContext, UnorderedListItemComponent); -Widget _defaultUnorderedListItemDotBuilder(BuildContext context, UnorderedListItemComponent component) { +Widget _defaultUnorderedListItemDotBuilder( + BuildContext context, UnorderedListItemComponent component) { return Align( alignment: Alignment.centerRight, child: Container( @@ -203,7 +210,7 @@ class OrderedListItemComponent extends StatelessWidget { Widget build(BuildContext context) { final indentSpace = indentExtent * (indent + 1); final firstLineHeight = styleBuilder({}).fontSize!; - final manualVerticalAdjustment = 2.0; + const manualVerticalAdjustment = 2.0; final manualHeightAdjustment = firstLineHeight * 0.15; return Row( @@ -212,9 +219,11 @@ class OrderedListItemComponent extends StatelessWidget { Container( width: indentSpace, height: firstLineHeight + manualHeightAdjustment, - margin: EdgeInsets.only(top: manualVerticalAdjustment), + margin: const EdgeInsets.only(top: manualVerticalAdjustment), decoration: BoxDecoration( - border: Border.all(width: 1, color: showDebugPaint ? Colors.grey : Colors.transparent), + border: Border.all( + width: 1, + color: showDebugPaint ? Colors.grey : Colors.transparent), ), child: SizedBox( width: indentSpace, @@ -239,9 +248,11 @@ class OrderedListItemComponent extends StatelessWidget { } } -typedef OrderedListItemNumeralBuilder = Widget Function(BuildContext, OrderedListItemComponent); +typedef OrderedListItemNumeralBuilder = Widget Function( + BuildContext, OrderedListItemComponent); -Widget _defaultOrderedListItemNumeralBuilder(BuildContext context, OrderedListItemComponent component) { +Widget _defaultOrderedListItemNumeralBuilder( + BuildContext context, OrderedListItemComponent component) { return OverflowBox( maxHeight: double.infinity, child: Align( @@ -271,7 +282,8 @@ class IndentListItemCommand implements EditorCommand { final node = document.getNodeById(nodeId); final listItem = node as ListItemNode; if (listItem.indent >= 6) { - _log.log('IndentListItemCommand', 'WARNING: Editor does not support an indent level beyond 6.'); + _log.log('IndentListItemCommand', + 'WARNING: Editor does not support an indent level beyond 6.'); return; } @@ -396,13 +408,16 @@ class SplitListItemCommand implements EditorCommand { final listItemNode = node as ListItemNode; final text = listItemNode.text; final startText = text.copyText(0, splitPosition.offset); - final endText = splitPosition.offset < text.text.length ? text.copyText(splitPosition.offset) : AttributedText(); + final endText = splitPosition.offset < text.text.length + ? text.copyText(splitPosition.offset) + : AttributedText(); _log.log('SplitListItemCommand', 'Splitting list item:'); _log.log('SplitListItemCommand', ' - start text: "$startText"'); _log.log('SplitListItemCommand', ' - end text: "$endText"'); // Change the current node's content to just the text before the caret. - _log.log('SplitListItemCommand', ' - changing the original list item text due to split'); + _log.log('SplitListItemCommand', + ' - changing the original list item text due to split'); // TODO: figure out how node changes should work in terms of // a DocumentEditorTransaction (#67) listItemNode.text = startText; @@ -428,7 +443,8 @@ class SplitListItemCommand implements EditorCommand { newNode: newNode, ); - _log.log('SplitListItemCommand', ' - inserted new node: ${newNode.id} after old one: ${node.id}'); + _log.log('SplitListItemCommand', + ' - inserted new node: ${newNode.id} after old one: ${node.id}'); } } @@ -445,7 +461,9 @@ ExecutionInstruction tabToIndentListItem({ final wasIndented = editContext.commonOps.indentListItem(); - return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return wasIndented + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction shiftTabToUnIndentListItem({ @@ -461,7 +479,9 @@ ExecutionInstruction shiftTabToUnIndentListItem({ final wasIndented = editContext.commonOps.unindentListItem(); - return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return wasIndented + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction backspaceToUnIndentListItem({ @@ -479,17 +499,22 @@ ExecutionInstruction backspaceToUnIndentListItem({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document + .getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! ListItemNode) { return ExecutionInstruction.continueExecution; } - if ((editContext.composer.selection!.extent.nodePosition as TextPosition).offset > 0) { + if ((editContext.composer.selection!.extent.nodePosition as TextPosition) + .offset > + 0) { return ExecutionInstruction.continueExecution; } final wasIndented = editContext.commonOps.unindentListItem(); - return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return wasIndented + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction splitListItemWhenEnterPressed({ @@ -500,13 +525,16 @@ ExecutionInstruction splitListItemWhenEnterPressed({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document + .getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! ListItemNode) { return ExecutionInstruction.continueExecution; } final didSplitListItem = editContext.commonOps.insertBlockLevelNewline(); - return didSplitListItem ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didSplitListItem + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } Widget? unorderedListItemBuilder(ComponentContext componentContext) { @@ -519,8 +547,10 @@ Widget? unorderedListItemBuilder(ComponentContext componentContext) { return null; } - final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = + componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && + (componentContext.nodeSelection?.isExtent ?? false); return UnorderedListItemComponent( textKey: componentContext.componentKey, @@ -528,9 +558,13 @@ Widget? unorderedListItemBuilder(ComponentContext componentContext) { styleBuilder: componentContext.extensions[textStylesExtensionKey], indent: listItemNode.indent, textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .textCaretColor, ); } @@ -545,7 +579,8 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { } int index = 1; - DocumentNode? nodeAbove = componentContext.document.getNodeBefore(listItemNode); + DocumentNode? nodeAbove = + componentContext.document.getNodeBefore(listItemNode); while (nodeAbove != null && nodeAbove is ListItemNode && nodeAbove.type == ListItemType.ordered && @@ -556,8 +591,10 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { nodeAbove = componentContext.document.getNodeBefore(nodeAbove); } - final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = + componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && + (componentContext.nodeSelection?.isExtent ?? false); return OrderedListItemComponent( textKey: componentContext.componentKey, @@ -565,9 +602,13 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { text: listItemNode.text, styleBuilder: componentContext.extensions[textStylesExtensionKey], textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] + as SelectionStyle) + .textCaretColor, indent: listItemNode.indent, ); } diff --git a/super_editor/lib/src/default_editor/styles.dart b/super_editor/lib/src/default_editor/styles.dart index 1390a9a5be..322c95fc73 100644 --- a/super_editor/lib/src/default_editor/styles.dart +++ b/super_editor/lib/src/default_editor/styles.dart @@ -3,11 +3,11 @@ import 'dart:ui'; /// The key in the `extensions` map that corresponds to the /// text style builder within the `ComponentContext` that /// is used to build each component in the document layout. -final String textStylesExtensionKey = 'editor.text_styles'; +const String textStylesExtensionKey = 'editor.text_styles'; /// The key in the `extensions` map that corresponds to the /// styles applied to selected content. -final String selectionStylesExtensionKey = 'editor.selection_styles'; +const String selectionStylesExtensionKey = 'editor.selection_styles'; class SelectionStyle { const SelectionStyle({ diff --git a/super_editor/lib/src/default_editor/super_editor.dart b/super_editor/lib/src/default_editor/super_editor.dart index 5380278a8f..4836c7cc51 100644 --- a/super_editor/lib/src/default_editor/super_editor.dart +++ b/super_editor/lib/src/default_editor/super_editor.dart @@ -186,7 +186,7 @@ class SuperEditor extends StatefulWidget { /// Paints some extra visual ornamentation to help with /// debugging, when true. - final showDebugPaint; + final bool showDebugPaint; @override _SuperEditorState createState() => _SuperEditorState(); @@ -263,12 +263,14 @@ class _SuperEditorState extends State { return; } - final node = widget.editor.document.getNodeById(_composer.selection!.extent.nodeId); + final node = + widget.editor.document.getNodeById(_composer.selection!.extent.nodeId); if (node is! TextNode) { return; } - final textPosition = _composer.selection!.extent.nodePosition as TextPosition; + final textPosition = + _composer.selection!.extent.nodePosition as TextPosition; if (textPosition.offset == 0) { if (node.text.text.isEmpty) { @@ -298,7 +300,8 @@ class _SuperEditorState extends State { commonOps: CommonEditorOperations( editor: widget.editor, composer: _composer, - documentLayoutResolver: () => _docLayoutKey.currentState as DocumentLayout, + documentLayoutResolver: () => + _docLayoutKey.currentState as DocumentLayout, ), ), keyboardActions: widget.keyboardActions, @@ -338,14 +341,14 @@ class _SuperEditorState extends State { } /// Default visual styles related to content selection. -final defaultSelectionStyle = const SelectionStyle( +const defaultSelectionStyle = SelectionStyle( textCaretColor: Colors.black, selectionColor: Color(0xFFACCEF7), ); /// Creates [TextStyles] for the standard [SuperEditor]. TextStyle defaultStyleBuilder(Set attributions) { - TextStyle newStyle = TextStyle( + TextStyle newStyle = const TextStyle( color: Colors.black, fontSize: 13, height: 1.4, diff --git a/super_editor/lib/src/default_editor/text.dart b/super_editor/lib/src/default_editor/text.dart index 98bfa8c768..3c06e26091 100644 --- a/super_editor/lib/src/default_editor/text.dart +++ b/super_editor/lib/src/default_editor/text.dart @@ -59,30 +59,37 @@ class TextNode with ChangeNotifier implements DocumentNode { Map get metadata => _metadata; @override - TextNodePosition get beginningPosition => TextNodePosition(offset: 0); + TextNodePosition get beginningPosition => const TextNodePosition(offset: 0); @override - TextNodePosition get endPosition => TextNodePosition(offset: text.text.length); + TextNodePosition get endPosition => + TextNodePosition(offset: text.text.length); @override - NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! TextNodePosition) { - throw Exception('Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! TextNodePosition) { - throw Exception('Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); } return position1.offset < position2.offset ? position1 : position2; } @override - NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition( + NodePosition position1, NodePosition position2) { if (position1 is! TextNodePosition) { - throw Exception('Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception( + 'Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! TextNodePosition) { - throw Exception('Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception( + 'Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); } return position1.offset > position2.offset ? position1 : position2; @@ -111,7 +118,9 @@ class TextNode with ChangeNotifier implements DocumentNode { @override bool hasEquivalentContent(DocumentNode other) { - return other is TextNode && text == other.text && DeepCollectionEquality().equals(metadata, other.metadata); + return other is TextNode && + text == other.text && + const DeepCollectionEquality().equals(metadata, other.metadata); } @override @@ -202,7 +211,9 @@ class TextComponent extends StatefulWidget { _TextComponentState createState() => _TextComponentState(); } -class _TextComponentState extends State with DocumentComponent implements TextComposable { +class _TextComponentState extends State + with DocumentComponent + implements TextComposable { final _selectableTextKey = GlobalKey(); @override @@ -218,7 +229,8 @@ class _TextComponentState extends State with DocumentComponent im @override Offset getOffsetForPosition(dynamic nodePosition) { if (nodePosition is! TextPosition) { - throw Exception('Expected nodePosition of type TextPosition but received: $nodePosition'); + throw Exception( + 'Expected nodePosition of type TextPosition but received: $nodePosition'); } return _selectableTextKey.currentState!.getOffsetAtPosition(nodePosition); } @@ -226,7 +238,8 @@ class _TextComponentState extends State with DocumentComponent im @override Rect getRectForPosition(dynamic nodePosition) { if (nodePosition is! TextPosition) { - throw Exception('Expected nodePosition of type TextPosition but received: $nodePosition'); + throw Exception( + 'Expected nodePosition of type TextPosition but received: $nodePosition'); } // TODO: factor in line height for position rect @@ -235,16 +248,22 @@ class _TextComponentState extends State with DocumentComponent im } @override - Rect getRectForSelection(dynamic baseNodePosition, dynamic extentNodePosition) { + Rect getRectForSelection( + dynamic baseNodePosition, dynamic extentNodePosition) { if (baseNodePosition is! TextPosition) { - throw Exception('Expected nodePosition of type TextPosition but received: $baseNodePosition'); + throw Exception( + 'Expected nodePosition of type TextPosition but received: $baseNodePosition'); } if (extentNodePosition is! TextPosition) { - throw Exception('Expected nodePosition of type TextPosition but received: $extentNodePosition'); + throw Exception( + 'Expected nodePosition of type TextPosition but received: $extentNodePosition'); } - final selection = TextSelection(baseOffset: baseNodePosition.offset, extentOffset: extentNodePosition.offset); - final boxes = _selectableTextKey.currentState!.getBoxesForSelection(selection); + final selection = TextSelection( + baseOffset: baseNodePosition.offset, + extentOffset: extentNodePosition.offset); + final boxes = + _selectableTextKey.currentState!.getBoxesForSelection(selection); Rect boundingBox = boxes.isNotEmpty ? boxes.first.toRect() : Rect.zero; for (int i = 1; i < boxes.length; ++i) { @@ -256,7 +275,7 @@ class _TextComponentState extends State with DocumentComponent im @override TextNodePosition getBeginningPosition() { - return TextNodePosition(offset: 0); + return const TextNodePosition(offset: 0); } @override @@ -267,7 +286,8 @@ class _TextComponentState extends State with DocumentComponent im } @override - TextNodePosition? movePositionLeft(NodePosition textPosition, [Set? movementModifiers]) { + TextNodePosition? movePositionLeft(NodePosition textPosition, + [Set? movementModifiers]) { if (textPosition is! TextNodePosition) { // We don't know how to interpret a non-text position. return null; @@ -283,11 +303,13 @@ class _TextComponentState extends State with DocumentComponent im return null; } - if (movementModifiers != null && movementModifiers.contains(MovementModifier.line)) { + if (movementModifiers != null && + movementModifiers.contains(MovementModifier.line)) { return getPositionAtStartOfLine( TextNodePosition(offset: textPosition.offset), ); - } else if (movementModifiers != null && movementModifiers.contains(MovementModifier.word)) { + } else if (movementModifiers != null && + movementModifiers.contains(MovementModifier.word)) { final text = getContiguousTextAt(textPosition); int newOffset = textPosition.offset; @@ -302,7 +324,8 @@ class _TextComponentState extends State with DocumentComponent im } @override - TextNodePosition? movePositionRight(NodePosition textPosition, [Set? movementModifiers]) { + TextNodePosition? movePositionRight(NodePosition textPosition, + [Set? movementModifiers]) { if (textPosition is! TextNodePosition) { // We don't know how to interpret a non-text position. return null; @@ -313,7 +336,8 @@ class _TextComponentState extends State with DocumentComponent im return null; } - if (movementModifiers != null && movementModifiers.contains(MovementModifier.line)) { + if (movementModifiers != null && + movementModifiers.contains(MovementModifier.line)) { final endOfLine = getPositionAtEndOfLine( TextNodePosition(offset: textPosition.offset), ); @@ -322,7 +346,8 @@ class _TextComponentState extends State with DocumentComponent im final text = getContiguousTextAt(endOfLine); // Note: we compare offset values because we don't care if the affinitys are equal - final isAutoWrapLine = endOfLine.offset != endPosition.offset && (text[endOfLine.offset] != '\n'); + final isAutoWrapLine = endOfLine.offset != endPosition.offset && + (text[endOfLine.offset] != '\n'); // Note: For lines that auto-wrap, moving the cursor to `offset` causes the // cursor to jump to the next line because the cursor is placed after @@ -340,7 +365,8 @@ class _TextComponentState extends State with DocumentComponent im ? TextNodePosition(offset: endOfLine.offset - 1) : TextNodePosition.fromTextPosition(endOfLine); } - if (movementModifiers != null && movementModifiers.contains(MovementModifier.word)) { + if (movementModifiers != null && + movementModifiers.contains(MovementModifier.word)) { final text = getContiguousTextAt(textPosition); int newOffset = textPosition.offset; @@ -361,7 +387,8 @@ class _TextComponentState extends State with DocumentComponent im return null; } - if (textNodePosition.offset < 0 || textNodePosition.offset > widget.text.text.length) { + if (textNodePosition.offset < 0 || + textNodePosition.offset > widget.text.text.length) { // This text position does not represent a position within our text. return null; } @@ -380,7 +407,8 @@ class _TextComponentState extends State with DocumentComponent im return null; } - if (textNodePosition.offset < 0 || textNodePosition.offset > widget.text.text.length) { + if (textNodePosition.offset < 0 || + textNodePosition.offset > widget.text.text.length) { // This text position does not represent a position within our text. return null; } @@ -399,19 +427,22 @@ class _TextComponentState extends State with DocumentComponent im @override TextNodePosition getEndPositionNearX(double x) { - return TextNodePosition.fromTextPosition(_selectableTextKey.currentState!.getPositionInLastLineAtX(x)); + return TextNodePosition.fromTextPosition( + _selectableTextKey.currentState!.getPositionInLastLineAtX(x)); } @override - TextNodeSelection getSelectionInRange(Offset localBaseOffset, Offset localExtentOffset) { - return TextNodeSelection.fromTextSelection( - _selectableTextKey.currentState!.getSelectionInRect(localBaseOffset, localExtentOffset)); + TextNodeSelection getSelectionInRange( + Offset localBaseOffset, Offset localExtentOffset) { + return TextNodeSelection.fromTextSelection(_selectableTextKey.currentState! + .getSelectionInRect(localBaseOffset, localExtentOffset)); } @override TextNodeSelection getCollapsedSelectionAt(NodePosition textNodePosition) { if (textNodePosition is! TextNodePosition) { - throw Exception('The given node position ($textNodePosition) is not compatible with TextComponent'); + throw Exception( + 'The given node position ($textNodePosition) is not compatible with TextComponent'); } return TextNodeSelection.collapsed(offset: textNodePosition.offset); @@ -423,10 +454,12 @@ class _TextComponentState extends State with DocumentComponent im required NodePosition extentPosition, }) { if (basePosition is! TextNodePosition) { - throw Exception('Expected a basePosition of type TextNodePosition but received: $basePosition'); + throw Exception( + 'Expected a basePosition of type TextNodePosition but received: $basePosition'); } if (extentPosition is! TextNodePosition) { - throw Exception('Expected an extentPosition of type TextNodePosition but received: $extentPosition'); + throw Exception( + 'Expected an extentPosition of type TextNodePosition but received: $extentPosition'); } return TextNodeSelection( @@ -445,7 +478,9 @@ class _TextComponentState extends State with DocumentComponent im @override MouseCursor? getDesiredCursorAtOffset(Offset localOffset) { - return _selectableTextKey.currentState!.isTextAtOffset(localOffset) ? SystemMouseCursors.text : null; + return _selectableTextKey.currentState!.isTextAtOffset(localOffset) + ? SystemMouseCursors.text + : null; } @override @@ -469,10 +504,12 @@ class _TextComponentState extends State with DocumentComponent im @override TextNodePosition? getPositionOneLineUp(NodePosition textPosition) { if (textPosition is! TextNodePosition) { - throw Exception('Expected position of type NodePosition but received ${textPosition.runtimeType}'); + throw Exception( + 'Expected position of type NodePosition but received ${textPosition.runtimeType}'); } - final positionOneLineUp = _selectableTextKey.currentState!.getPositionOneLineUp(textPosition); + final positionOneLineUp = + _selectableTextKey.currentState!.getPositionOneLineUp(textPosition); if (positionOneLineUp == null) { return null; } @@ -482,10 +519,12 @@ class _TextComponentState extends State with DocumentComponent im @override TextNodePosition? getPositionOneLineDown(NodePosition textPosition) { if (textPosition is! TextNodePosition) { - throw Exception('Expected position of type NodePosition but received ${textPosition.runtimeType}'); + throw Exception( + 'Expected position of type NodePosition but received ${textPosition.runtimeType}'); } - final positionOneLineDown = _selectableTextKey.currentState!.getPositionOneLineDown(textPosition); + final positionOneLineDown = + _selectableTextKey.currentState!.getPositionOneLineDown(textPosition); if (positionOneLineDown == null) { return null; } @@ -502,7 +541,8 @@ class _TextComponentState extends State with DocumentComponent im @override TextNodePosition getPositionAtStartOfLine(TextNodePosition textNodePosition) { return TextNodePosition.fromTextPosition( - _selectableTextKey.currentState!.getPositionAtStartOfLine(textNodePosition), + _selectableTextKey.currentState! + .getPositionAtStartOfLine(textNodePosition), ); } @@ -526,8 +566,10 @@ class _TextComponentState extends State with DocumentComponent im key: _selectableTextKey, textSpan: richText, textAlign: widget.textAlign ?? TextAlign.left, - textSelection: widget.textSelection ?? TextSelection.collapsed(offset: -1), - textSelectionDecoration: TextSelectionDecoration(selectionColor: widget.selectionColor), + textSelection: + widget.textSelection ?? const TextSelection.collapsed(offset: -1), + textSelectionDecoration: + TextSelectionDecoration(selectionColor: widget.selectionColor), showCaret: widget.showCaret, textCaretFactory: TextCaretFactory(color: widget.caretColor), highlightWhenEmpty: widget.highlightWhenEmpty, @@ -549,8 +591,10 @@ class AddTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log('AddTextAttributionsCommand', 'Executing AddTextAttributionsCommand'); - final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); + _log.log( + 'AddTextAttributionsCommand', 'Executing AddTextAttributionsCommand'); + final nodes = document.getNodesInside( + documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('AddTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -559,7 +603,8 @@ class AddTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween( + documentSelection.base, documentSelection.extent); _log.log('AddTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -575,9 +620,12 @@ class AddTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('AddTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('AddTextAttributionsCommand', + ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = + (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = + (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -586,12 +634,14 @@ class AddTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('AddTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', + ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('AddTextAttributionsCommand', ' - adding part of the last node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', + ' - adding part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -599,7 +649,8 @@ class AddTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('AddTextAttributionsCommand', ' - adding full node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', + ' - adding full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -614,7 +665,8 @@ class AddTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('AddTextAttributionsCommand', ' - adding attribution: $attribution. Range: $range'); + _log.log('AddTextAttributionsCommand', + ' - adding attribution: $attribution. Range: $range'); node.text.addAttribution( attribution, range, @@ -638,8 +690,10 @@ class RemoveTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log('RemoveTextAttributionsCommand', 'Executing RemoveTextAttributionsCommand'); - final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); + _log.log('RemoveTextAttributionsCommand', + 'Executing RemoveTextAttributionsCommand'); + final nodes = document.getNodesInside( + documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('RemoveTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -648,7 +702,8 @@ class RemoveTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween( + documentSelection.base, documentSelection.extent); _log.log('RemoveTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -664,9 +719,12 @@ class RemoveTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('RemoveTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('RemoveTextAttributionsCommand', + ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = + (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = + (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -675,12 +733,14 @@ class RemoveTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('RemoveTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', + ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('RemoveTextAttributionsCommand', ' - adding part of the last node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', + ' - adding part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -688,7 +748,8 @@ class RemoveTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('RemoveTextAttributionsCommand', ' - adding full node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', + ' - adding full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -703,7 +764,8 @@ class RemoveTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('RemoveTextAttributionsCommand', ' - removing attribution: $attribution. Range: $range'); + _log.log('RemoveTextAttributionsCommand', + ' - removing attribution: $attribution. Range: $range'); node.text.removeAttribution( attribution, range, @@ -730,8 +792,10 @@ class ToggleTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log('ToggleTextAttributionsCommand', 'Executing ToggleTextAttributionsCommand'); - final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); + _log.log('ToggleTextAttributionsCommand', + 'Executing ToggleTextAttributionsCommand'); + final nodes = document.getNodesInside( + documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('ToggleTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -740,7 +804,8 @@ class ToggleTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween( + documentSelection.base, documentSelection.extent); _log.log('ToggleTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -757,9 +822,12 @@ class ToggleTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('ToggleTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('ToggleTextAttributionsCommand', + ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = + (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = + (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -768,12 +836,14 @@ class ToggleTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('ToggleTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', + ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('ToggleTextAttributionsCommand', ' - toggling part of the last node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', + ' - toggling part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -781,7 +851,8 @@ class ToggleTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('ToggleTextAttributionsCommand', ' - toggling full node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', + ' - toggling full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -802,7 +873,8 @@ class ToggleTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('ToggleTextAttributionsCommand', ' - toggling attribution: $attribution. Range: $range'); + _log.log('ToggleTextAttributionsCommand', + ' - toggling attribution: $attribution. Range: $range'); node.text.toggleAttribution( attribution, range, @@ -829,7 +901,8 @@ class InsertTextCommand implements EditorCommand { void execute(Document document, DocumentEditorTransaction transaction) { final textNode = document.getNodeById(documentPosition.nodeId); if (textNode is! TextNode) { - _log.log('InsertTextCommand', 'ERROR: can\'t insert text in a node that isn\'t a TextNode: $textNode'); + _log.log('InsertTextCommand', + 'ERROR: can\'t insert text in a node that isn\'t a TextNode: $textNode'); return; } @@ -856,7 +929,9 @@ ExecutionInstruction anyCharacterToInsertInTextContent({ if (!editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } - if (!_isTextEntryNode(document: editContext.editor.document, selection: editContext.composer.selection!)) { + if (!_isTextEntryNode( + document: editContext.editor.document, + selection: editContext.composer.selection!)) { return ExecutionInstruction.continueExecution; } if (keyEvent.character == null || keyEvent.character == '') { @@ -884,7 +959,9 @@ ExecutionInstruction anyCharacterToInsertInTextContent({ final didInsertCharacter = editContext.commonOps.insertCharacter(character); - return didInsertCharacter ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didInsertCharacter + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction deleteCharacterWhenBackspaceIsPressed({ @@ -897,19 +974,25 @@ ExecutionInstruction deleteCharacterWhenBackspaceIsPressed({ if (editContext.composer.selection == null) { return ExecutionInstruction.continueExecution; } - if (!_isTextEntryNode(document: editContext.editor.document, selection: editContext.composer.selection!)) { + if (!_isTextEntryNode( + document: editContext.editor.document, + selection: editContext.composer.selection!)) { return ExecutionInstruction.continueExecution; } if (!editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } - if ((editContext.composer.selection!.extent.nodePosition as TextPosition).offset <= 0) { + if ((editContext.composer.selection!.extent.nodePosition as TextPosition) + .offset <= + 0) { return ExecutionInstruction.continueExecution; } final didDelete = editContext.commonOps.deleteUpstream(); - return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didDelete + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction deleteToRemoveDownstreamContent({ @@ -922,7 +1005,9 @@ ExecutionInstruction deleteToRemoveDownstreamContent({ final didDelete = editContext.commonOps.deleteDownstream(); - return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didDelete + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } ExecutionInstruction shiftEnterToInsertNewlineInBlock({ @@ -938,7 +1023,9 @@ ExecutionInstruction shiftEnterToInsertNewlineInBlock({ final didInsertNewline = editContext.commonOps.insertPlainText('\n'); - return didInsertNewline ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; + return didInsertNewline + ? ExecutionInstruction.haltExecution + : ExecutionInstruction.continueExecution; } bool _isTextEntryNode({ diff --git a/super_editor/lib/src/default_editor/text_tools.dart b/super_editor/lib/src/default_editor/text_tools.dart index b77a2162c8..bbf53ebd94 100644 --- a/super_editor/lib/src/default_editor/text_tools.dart +++ b/super_editor/lib/src/default_editor/text_tools.dart @@ -34,8 +34,10 @@ DocumentSelection? getWordSelection({ return null; } - final TextSelection wordTextSelection = (component as TextComposable).getWordSelectionAt(nodePosition); - final wordNodeSelection = TextNodeSelection.fromTextSelection(wordTextSelection); + final TextSelection wordTextSelection = + (component as TextComposable).getWordSelectionAt(nodePosition); + final wordNodeSelection = + TextNodeSelection.fromTextSelection(wordTextSelection); _log.log('getWordSelection', ' - word selection: $wordNodeSelection'); return DocumentSelection( @@ -57,7 +59,7 @@ TextSelection expandPositionToWord({ required TextPosition textPosition, }) { if (text.isEmpty) { - return TextSelection.collapsed(offset: -1); + return const TextSelection.collapsed(offset: -1); } int start = min(textPosition.offset, text.length - 1); @@ -100,7 +102,8 @@ DocumentSelection? getParagraphSelection({ text: (component as TextComposable).getContiguousTextAt(nodePosition), textPosition: docPosition.nodePosition as TextPosition, ); - final paragraphNodeSelection = TextNodeSelection.fromTextSelection(paragraphTextSelection); + final paragraphNodeSelection = + TextNodeSelection.fromTextSelection(paragraphTextSelection); return DocumentSelection( base: DocumentPosition( @@ -121,7 +124,7 @@ TextSelection expandPositionToParagraph({ required TextPosition textPosition, }) { if (text.isEmpty) { - return TextSelection.collapsed(offset: -1); + return const TextSelection.collapsed(offset: -1); } int start = min(textPosition.offset, text.length - 1); diff --git a/super_editor/lib/src/default_editor/unknown_component.dart b/super_editor/lib/src/default_editor/unknown_component.dart index 0470dd4739..cc12edde5b 100644 --- a/super_editor/lib/src/default_editor/unknown_component.dart +++ b/super_editor/lib/src/default_editor/unknown_component.dart @@ -10,7 +10,7 @@ Widget unknownComponentBuilder(ComponentContext componentContext) { key: componentContext.componentKey, width: double.infinity, height: 100, - child: Placeholder(), + child: const Placeholder(), ); } @@ -22,7 +22,7 @@ Widget unknownComponentBuilder(ComponentContext componentContext) { class UnknownComponent extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( + return const SizedBox( width: double.infinity, height: 54, child: Placeholder(), diff --git a/super_editor/lib/src/infrastructure/_platform_detector_web.dart b/super_editor/lib/src/infrastructure/_platform_detector_web.dart index 190b5399ee..0157067926 100644 --- a/super_editor/lib/src/infrastructure/_platform_detector_web.dart +++ b/super_editor/lib/src/infrastructure/_platform_detector_web.dart @@ -1,3 +1,4 @@ +// ignore: avoid_web_libraries_in_flutter import 'dart:html'; final _platform = window.navigator.platform ?? ''; diff --git a/super_editor/lib/src/infrastructure/attributed_spans.dart b/super_editor/lib/src/infrastructure/attributed_spans.dart index 914ae3f876..2c3e8f5e8d 100644 --- a/super_editor/lib/src/infrastructure/attributed_spans.dart +++ b/super_editor/lib/src/infrastructure/attributed_spans.dart @@ -107,13 +107,16 @@ class AttributedSpans { int offset, { Attribution? attribution, }) { - SpanMarker? markerBefore = _getStartingMarkerAtOrBefore(offset, attribution: attribution); + SpanMarker? markerBefore = + _getStartingMarkerAtOrBefore(offset, attribution: attribution); if (markerBefore == null) { return false; } - SpanMarker? markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, attribution: attribution); + SpanMarker? markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, + attribution: attribution); if (markerAfter == null) { - throw Exception('Found an open-ended attribution. It starts with: $markerBefore'); + throw Exception( + 'Found an open-ended attribution. It starts with: $markerBefore'); } return (markerBefore.offset <= offset) && (offset <= markerAfter.offset); @@ -131,8 +134,10 @@ class AttributedSpans { // The following methods should be guaranteed to produce non-null // values because we already verified that the given attribution // exists at the given offset. - SpanMarker markerBefore = _getStartingMarkerAtOrBefore(offset, attribution: attribution)!; - SpanMarker markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, attribution: attribution)!; + SpanMarker markerBefore = + _getStartingMarkerAtOrBefore(offset, attribution: attribution)!; + SpanMarker markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, + attribution: attribution)!; return AttributionSpan( attribution: attribution, @@ -213,25 +218,30 @@ class AttributedSpans { /// Finds and returns the nearest [start] marker that appears at or before the /// given [offset], optionally looking specifically for a marker with /// the given [attribution]. - SpanMarker? _getStartingMarkerAtOrBefore(int offset, {Attribution? attribution}) { + SpanMarker? _getStartingMarkerAtOrBefore(int offset, + {Attribution? attribution}) { return _attributions // .reversed // search from the end so its the nearest start marker .where((marker) { return attribution == null || - (marker.attribution.id == attribution.id && marker.attribution.canMergeWith(attribution)); + (marker.attribution.id == attribution.id && + marker.attribution.canMergeWith(attribution)); }) // .where((marker) => attribution == null || marker.attribution.id == attribution.id) - .firstWhereOrNull((marker) => marker.isStart && marker.offset <= offset); + .firstWhereOrNull( + (marker) => marker.isStart && marker.offset <= offset); } /// Finds and returns the nearest [end] marker that appears at or after the /// given [offset], optionally looking specifically for a marker with /// the given [attribution]. - SpanMarker? _getEndingMarkerAtOrAfter(int offset, {Attribution? attribution}) { + SpanMarker? _getEndingMarkerAtOrAfter(int offset, + {Attribution? attribution}) { return _attributions .where((marker) => attribution == null || - (marker.attribution.id == attribution.id && marker.attribution.canMergeWith(attribution))) + (marker.attribution.id == attribution.id && + marker.attribution.canMergeWith(attribution))) .firstWhereOrNull((marker) => marker.isEnd && marker.offset >= offset); } @@ -254,7 +264,8 @@ class AttributedSpans { // Ensure that no conflicting attribution overlaps the new attribution. // If a conflict exists, throw an exception. - final matchingAttributions = getMatchingAttributionsWithin(attributions: {newAttribution}, start: start, end: end); + final matchingAttributions = getMatchingAttributionsWithin( + attributions: {newAttribution}, start: start, end: end); if (matchingAttributions.isNotEmpty) { for (final matchingAttribution in matchingAttributions) { if (!newAttribution.canMergeWith(matchingAttribution)) { @@ -292,12 +303,15 @@ class AttributedSpans { .where((attribution) => attribution.offset > start) .where((attribution) => attribution.offset <= end) .toList(); - _log.log('addAttribution', 'removing ${markersToDelete.length} markers between $start and $end'); + _log.log('addAttribution', + 'removing ${markersToDelete.length} markers between $start and $end'); _attributions.removeWhere((element) => markersToDelete.contains(element)); - final lastDeletedMarker = markersToDelete.isNotEmpty ? markersToDelete.last : null; + final lastDeletedMarker = + markersToDelete.isNotEmpty ? markersToDelete.last : null; - if (lastDeletedMarker == null || lastDeletedMarker.markerType == SpanMarkerType.end) { + if (lastDeletedMarker == null || + lastDeletedMarker.markerType == SpanMarkerType.end) { // If we didn't delete any markers, the span that began at // `range.start` or before needs to be capped off. // @@ -315,7 +329,9 @@ class AttributedSpans { // doesn't need to be inserted. _log.log('addAttribution', 'all attributions after:'); - _attributions.where((element) => element.attribution == newAttribution).forEach((element) { + _attributions + .where((element) => element.attribution == newAttribution) + .forEach((element) { _log.log('addAttribution', '$element'); }); } @@ -327,7 +343,8 @@ class AttributedSpans { required int end, }) { if (start < 0 || start > end) { - throw Exception('removeAttribution() did not satisfy start < 0 and start > end, start: $start, end: $end'); + throw Exception( + 'removeAttribution() did not satisfy start < 0 and start > end, start: $start, end: $end'); } // It's possible that a span we want to remove was started before the @@ -391,11 +408,14 @@ class AttributedSpans { .where((attribution) => attribution.offset >= start) .where((attribution) => attribution.offset <= end) .toList(); - _log.log('removeAttribution', 'removing ${markersToDelete.length} markers between $start and $end'); + _log.log('removeAttribution', + 'removing ${markersToDelete.length} markers between $start and $end'); _attributions.removeWhere((element) => markersToDelete.contains(element)); _log.log('removeAttribution', 'all attributions after:'); - _attributions.where((element) => element.attribution == attributionToRemove).forEach((element) { + _attributions + .where((element) => element.attribution == attributionToRemove) + .forEach((element) { _log.log('removeAttribution', ' - $element'); }); } @@ -409,8 +429,10 @@ class AttributedSpans { required int start, required int end, }) { - if (_isContinuousAttribution(attribution: attribution, start: start, end: end)) { - removeAttribution(attributionToRemove: attribution, start: start, end: end); + if (_isContinuousAttribution( + attribution: attribution, start: start, end: end)) { + removeAttribution( + attributionToRemove: attribution, start: start, end: end); } else { addAttribution(newAttribution: attribution, start: start, end: end); } @@ -424,8 +446,10 @@ class AttributedSpans { required int start, required int end, }) { - _log.log('_isContinuousAttribution', 'attribution: "$attribution", range: $start -> $end'); - SpanMarker? markerBefore = _getNearestMarkerAtOrBefore(start, attribution: attribution); + _log.log('_isContinuousAttribution', + 'attribution: "$attribution", range: $start -> $end'); + SpanMarker? markerBefore = + _getNearestMarkerAtOrBefore(start, attribution: attribution); _log.log('_isContinuousAttribution', 'marker before: $markerBefore'); if (markerBefore == null || markerBefore.isEnd) { @@ -434,15 +458,19 @@ class AttributedSpans { final indexBefore = _attributions.indexOf(markerBefore); final nextMarker = _attributions.sublist(indexBefore).firstWhereOrNull( - (marker) => marker.attribution == attribution && marker.offset > markerBefore.offset, + (marker) => + marker.attribution == attribution && + marker.offset > markerBefore.offset, ); _log.log('_isContinuousAttribution', 'next marker: $nextMarker'); if (nextMarker == null) { - throw Exception('Inconsistent attributions state. Found a `start` marker with no matching `end`.'); + throw Exception( + 'Inconsistent attributions state. Found a `start` marker with no matching `end`.'); } if (nextMarker.isStart) { - throw Exception('Inconsistent attributions state. Found a `start` marker following a `start` marker.'); + throw Exception( + 'Inconsistent attributions state. Found a `start` marker following a `start` marker.'); } // If there is even one additional marker in the `range` @@ -459,8 +487,9 @@ class AttributedSpans { Attribution? attribution, }) { SpanMarker? markerBefore; - final markers = - attribution != null ? _attributions.where((marker) => marker.attribution == attribution) : _attributions; + final markers = attribution != null + ? _attributions.where((marker) => marker.attribution == attribution) + : _attributions; for (final marker in markers) { if (marker.offset <= offset) { @@ -487,8 +516,8 @@ class AttributedSpans { /// Precondition: There must not already exist a marker with /// the same attribution at the same offset. void _insertMarker(SpanMarker newMarker) { - SpanMarker? markerAfter = - _attributions.firstWhereOrNull((existingMarker) => existingMarker.offset >= newMarker.offset); + SpanMarker? markerAfter = _attributions.firstWhereOrNull( + (existingMarker) => existingMarker.offset >= newMarker.offset); if (markerAfter != null) { final markerAfterIndex = _attributions.indexOf(markerAfter); @@ -526,7 +555,8 @@ class AttributedSpans { final pushedSpans = other.copy()..pushAttributionsBack(pushDistance); // Combine `this` and `other` attributions into one list. - final List combinedAttributions = List.from(_attributions)..addAll(pushedSpans._attributions); + final List combinedAttributions = List.from(_attributions) + ..addAll(pushedSpans._attributions); _log.log('addAt', 'combined attributions before merge:'); for (final marker in combinedAttributions) { _log.log('addAt', ' - $marker'); @@ -549,25 +579,32 @@ class AttributedSpans { /// Given a list of [attributions], which includes two different lists of /// attributions concatenated together at [mergePoint], merges any /// attribution spans that exist back-to-back at the [mergePoint]. - void _mergeBackToBackAttributions(List attributions, int mergePoint) { - _log.log('_mergeBackToBackAttributions', 'merging attributions at $mergePoint'); + void _mergeBackToBackAttributions( + List attributions, int mergePoint) { + _log.log( + '_mergeBackToBackAttributions', 'merging attributions at $mergePoint'); // Look for any compatible attributions at // `mergePoint - 1` and `mergePoint` and combine them. - final endAtMergePointMarkers = - attributions.where((marker) => marker.isEnd && marker.offset == mergePoint - 1).toList(); - final startAtMergePointMarkers = - attributions.where((marker) => marker.isStart && marker.offset == mergePoint).toList(); + final endAtMergePointMarkers = attributions + .where((marker) => marker.isEnd && marker.offset == mergePoint - 1) + .toList(); + final startAtMergePointMarkers = attributions + .where((marker) => marker.isStart && marker.offset == mergePoint) + .toList(); for (final startMarker in startAtMergePointMarkers) { - _log.log('_mergeBackToBackAttributions', 'marker on right side: $startMarker'); + _log.log( + '_mergeBackToBackAttributions', 'marker on right side: $startMarker'); final endMarker = endAtMergePointMarkers.firstWhereOrNull( (marker) => marker.attribution == startMarker.attribution, ); - _log.log('_mergeBackToBackAttributions', 'matching marker on left side? $endMarker'); + _log.log('_mergeBackToBackAttributions', + 'matching marker on left side? $endMarker'); if (endMarker != null) { // These two attributions should be combined into one. // To do this, delete these two markers from the original // attribution list. - _log.log('_mergeBackToBackAttributions', 'combining left/right spans at edge at index $mergePoint'); + _log.log('_mergeBackToBackAttributions', + 'combining left/right spans at edge at index $mergePoint'); _log.log('_mergeBackToBackAttributions', 'Removing markers:'); _log.log('_mergeBackToBackAttributions', ' - $startMarker'); _log.log('_mergeBackToBackAttributions', ' - $endMarker'); @@ -587,7 +624,8 @@ class AttributedSpans { final List cutAttributions = []; - _log.log('copyAttributionRegion', 'inspecting existing markers in full AttributedSpans'); + _log.log('copyAttributionRegion', + 'inspecting existing markers in full AttributedSpans'); final Map foundStartMarkers = {}; final Map foundEndMarkers = {}; @@ -597,18 +635,22 @@ class AttributedSpans { _attributions // .where((marker) => marker.offset < startOffset) // .forEach((marker) { - _log.log('copyAttributionRegion', 'marker before the copy region: $marker'); + _log.log( + 'copyAttributionRegion', 'marker before the copy region: $marker'); // Track any markers that begin before the `startOffset` // and continue beyond `startOffset`. if (marker.isStart) { - _log.log('copyAttributionRegion', 'remembering this marker to insert in copied region'); + _log.log('copyAttributionRegion', + 'remembering this marker to insert in copied region'); foundStartMarkers.putIfAbsent(marker.attribution, () => 0); - foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! + 1; + foundStartMarkers[marker.attribution] = + foundStartMarkers[marker.attribution]! + 1; } else { _log.log('copyAttributionRegion', 'this marker counters an earlier one we found. We will not re-insert this marker in the copied region'); foundStartMarkers.putIfAbsent(marker.attribution, () => 0); - foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! - 1; + foundStartMarkers[marker.attribution] = + foundStartMarkers[marker.attribution]! - 1; } }); @@ -633,7 +675,8 @@ class AttributedSpans { // Directly copy every marker that appears within the cut // region. _attributions // - .where((marker) => startOffset <= marker.offset && marker.offset <= endOffset!) // + .where((marker) => + startOffset <= marker.offset && marker.offset <= endOffset!) // .forEach((marker) { _log.log('copyAttributionRegion', 'copying "${marker.attribution}" at ${marker.offset} from original AttributionSpans to copy region.'); @@ -649,18 +692,22 @@ class AttributedSpans { .reversed // .where((marker) => marker.offset > endOffset!) // .forEach((marker) { - _log.log('copyAttributionRegion', 'marker after the copy region: $marker'); + _log.log( + 'copyAttributionRegion', 'marker after the copy region: $marker'); // Track any markers that end after the `endOffset` // and start before `endOffset`. if (marker.isEnd) { - _log.log('copyAttributionRegion', 'remembering this marker to insert in copied region'); + _log.log('copyAttributionRegion', + 'remembering this marker to insert in copied region'); foundEndMarkers.putIfAbsent(marker.attribution, () => 0); - foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! + 1; + foundEndMarkers[marker.attribution] = + foundEndMarkers[marker.attribution]! + 1; } else { _log.log('copyAttributionRegion', 'this marker counters an earlier one we found. We will not re-insert this marker in the copied region'); foundEndMarkers.putIfAbsent(marker.attribution, () => 0); - foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! - 1; + foundEndMarkers[marker.attribution] = + foundEndMarkers[marker.attribution]! - 1; } }); @@ -693,7 +740,9 @@ class AttributedSpans { /// Changes all spans in this [AttributedSpans] by pushing /// them back by [offset] amount. void pushAttributionsBack(int offset) { - final pushedAttributions = _attributions.map((marker) => marker.copyWith(offset: marker.offset + offset)).toList(); + final pushedAttributions = _attributions + .map((marker) => marker.copyWith(offset: marker.offset + offset)) + .toList(); _attributions ..clear() ..addAll(pushedAttributions); @@ -708,18 +757,23 @@ class AttributedSpans { final contractedAttributions = []; // Add all the markers that are unchanged. - contractedAttributions.addAll(_attributions.where((marker) => marker.offset < startOffset)); + contractedAttributions + .addAll(_attributions.where((marker) => marker.offset < startOffset)); - _log.log('contractAttributions', 'removing $count characters starting at $startOffset'); + _log.log('contractAttributions', + 'removing $count characters starting at $startOffset'); final needToEndAttributions = {}; final needToStartAttributions = {}; _attributions - .where((marker) => (startOffset <= marker.offset) && (marker.offset < startOffset + count)) + .where((marker) => + (startOffset <= marker.offset) && + (marker.offset < startOffset + count)) .forEach((marker) { // Get rid of this marker and keep track of // any open-ended attributions that need to // be closed. - _log.log('contractAttributions', 'removing ${marker.markerType} at ${marker.offset}'); + _log.log('contractAttributions', + 'removing ${marker.markerType} at ${marker.offset}'); if (marker.isStart) { if (needToEndAttributions.contains(marker.attribution)) { // We've already removed an `end` marker so now @@ -745,7 +799,7 @@ class AttributedSpans { // Re-insert any markers that are needed to retain // symmetry after the deletions above. - needToStartAttributions.forEach((attribution) { + for (final attribution in needToStartAttributions) { final offset = startOffset > 0 ? startOffset - 1 : 0; _log.log('contractAttributions', 'adding back a start marker at $offset'); contractedAttributions.add(SpanMarker( @@ -753,8 +807,8 @@ class AttributedSpans { offset: offset, markerType: SpanMarkerType.start, )); - }); - needToEndAttributions.forEach((attribution) { + } + for (final attribution in needToEndAttributions) { final offset = startOffset > 0 ? startOffset - 1 : 0; _log.log('contractAttributions', 'adding back an end marker at $offset'); contractedAttributions.add(SpanMarker( @@ -762,7 +816,7 @@ class AttributedSpans { offset: offset, markerType: SpanMarkerType.end, )); - }); + } // Add all remaining markers but with an `offset` // that is less by `count`. @@ -816,7 +870,8 @@ class AttributedSpans { _log.log('collapseSpans', 'end points before change: $endPoints'); if (marker.offset > contentLength - 1) { - _log.log('collapseSpans', 'this marker is beyond the desired content length. Breaking loop.'); + _log.log('collapseSpans', + 'this marker is beyond the desired content length. Breaking loop.'); break; } @@ -831,7 +886,8 @@ class AttributedSpans { // then there won't be an `end` just before this // `start` point. Insert one. if (marker.offset > 0 && !endPoints.contains(marker.offset - 1)) { - _log.log('collapseSpans', 'going back one and adding end point at: ${marker.offset - 1}'); + _log.log('collapseSpans', + 'going back one and adding end point at: ${marker.offset - 1}'); endPoints.add(marker.offset - 1); } } @@ -846,8 +902,10 @@ class AttributedSpans { // the end of the content. We do this because we're not // guaranteed to have another `start` marker after this // `end` marker. - if (marker.offset < contentLength - 1 && !startPoints.contains(marker.offset + 1)) { - _log.log('collapseSpans', 'jumping forward one to add a start point at: ${marker.offset + 1}'); + if (marker.offset < contentLength - 1 && + !startPoints.contains(marker.offset + 1)) { + _log.log('collapseSpans', + 'jumping forward one to add a start point at: ${marker.offset + 1}'); startPoints.add(marker.offset + 1); } } @@ -876,8 +934,10 @@ class AttributedSpans { // Create the collapsed spans. List collapsedSpans = []; for (int i = 0; i < startPoints.length; ++i) { - _log.log('collapseSpans', 'building span from ${startPoints[i]} -> ${endPoints[i]}'); - _log.log('collapseSpans', ' - attributions in span: ${getAllAttributionsAt(startPoints[i])}'); + _log.log('collapseSpans', + 'building span from ${startPoints[i]} -> ${endPoints[i]}'); + _log.log('collapseSpans', + ' - attributions in span: ${getAllAttributionsAt(startPoints[i])}'); final attributionsAtOffset = getAllAttributionsAt(startPoints[i]); collapsedSpans.add(MultiAttributionSpan( @@ -895,14 +955,16 @@ class AttributedSpans { identical(this, other) || other is AttributedSpans && runtimeType == other.runtimeType && - DeepCollectionEquality().equals(_attributions, other._attributions); + const DeepCollectionEquality() + .equals(_attributions, other._attributions); @override int get hashCode => _attributions.hashCode; @override String toString() { - final buffer = StringBuffer('[AttributedSpans] (${(_attributions.length / 2).round()} spans):'); + final buffer = StringBuffer( + '[AttributedSpans] (${(_attributions.length / 2).round()} spans):'); for (final marker in _attributions) { buffer.write('\n - $marker'); } @@ -953,7 +1015,8 @@ class SpanMarker implements Comparable { ); @override - String toString() => '[SpanMarker] - attribution: $attribution, offset: $offset, type: $markerType'; + String toString() => + '[SpanMarker] - attribution: $attribution, offset: $offset, type: $markerType'; @override int compareTo(SpanMarker other) { @@ -969,7 +1032,8 @@ class SpanMarker implements Comparable { markerType == other.markerType; @override - int get hashCode => attribution.hashCode ^ offset.hashCode ^ markerType.hashCode; + int get hashCode => + attribution.hashCode ^ offset.hashCode ^ markerType.hashCode; } /// The type of a marker within a span, either [start] or [end]. @@ -1097,7 +1161,10 @@ class NamedAttribution implements Attribution { @override bool operator ==(Object other) => - identical(this, other) || other is NamedAttribution && runtimeType == other.runtimeType && id == other.id; + identical(this, other) || + other is NamedAttribution && + runtimeType == other.runtimeType && + id == other.id; @override int get hashCode => id.hashCode; diff --git a/super_editor/lib/src/infrastructure/super_selectable_text.dart b/super_editor/lib/src/infrastructure/super_selectable_text.dart index fdc85471fc..d79bf89771 100644 --- a/super_editor/lib/src/infrastructure/super_selectable_text.dart +++ b/super_editor/lib/src/infrastructure/super_selectable_text.dart @@ -51,7 +51,7 @@ class SuperSelectableText extends StatefulWidget { super(key: key); /// [SuperSelectableText] that displays styled text. - SuperSelectableText({ + const SuperSelectableText({ Key? key, required TextSpan textSpan, this.textAlign = TextAlign.left, @@ -104,7 +104,8 @@ class SuperSelectableText extends StatefulWidget { SuperSelectableTextState createState() => SuperSelectableTextState(); } -class SuperSelectableTextState extends State implements TextLayout { +class SuperSelectableTextState extends State + implements TextLayout { // [GlobalKey] that provides access to the [RenderParagraph] associated // with the text that this [SuperSelectableText] widget displays. final GlobalKey _textKey = GlobalKey(); @@ -134,8 +135,9 @@ class SuperSelectableTextState extends State implements Tex _cachedTextLength = widget.richText.toPlainText().length; } - RenderParagraph? get _renderParagraph => - _textKey.currentContext != null ? _textKey.currentContext!.findRenderObject() as RenderParagraph : null; + RenderParagraph? get _renderParagraph => _textKey.currentContext != null + ? _textKey.currentContext!.findRenderObject() as RenderParagraph + : null; // TODO: use TextPainter line height when Flutter makes the info available. (#46) double get _lineHeight { @@ -151,7 +153,7 @@ class SuperSelectableTextState extends State implements Tex @override TextPosition getPositionAtOffset(Offset localOffset) { if (_renderParagraph == null) { - return TextPosition(offset: -1); + return const TextPosition(offset: -1); } // TODO: bring back this condition by changing existing uses of @@ -166,7 +168,7 @@ class SuperSelectableTextState extends State implements Tex @override TextPosition getPositionNearestToOffset(Offset localOffset) { if (_renderParagraph == null) { - return TextPosition(offset: -1); + return const TextPosition(offset: -1); } return _renderParagraph!.getPositionForOffset(localOffset); @@ -175,10 +177,12 @@ class SuperSelectableTextState extends State implements Tex @override Offset getOffsetAtPosition(TextPosition position) { if (_renderParagraph == null) { - throw Exception('SelectableText does not yet have a RenderParagraph. Can\'t getOffsetForPosition().'); + throw Exception( + 'SelectableText does not yet have a RenderParagraph. Can\'t getOffsetForPosition().'); } - if (_renderParagraph!.hasSize && (kDebugMode && _renderParagraph!.debugNeedsLayout)) { + if (_renderParagraph!.hasSize && + (kDebugMode && _renderParagraph!.debugNeedsLayout)) { // This condition was added because getOffsetForCaret() was throwing // an exception when debugNeedsLayout is true. It's unclear what we're // supposed to do at our level to ensure that condition doesn't happen @@ -197,7 +201,8 @@ class SuperSelectableTextState extends State implements Tex @override List getBoxesForSelection(TextSelection selection) { if (_renderParagraph == null) { - throw Exception('SelectableText does not yet have a RenderParagraph. Can\'t getBoxesForSelection().'); + throw Exception( + 'SelectableText does not yet have a RenderParagraph. Can\'t getBoxesForSelection().'); } return _renderParagraph!.getBoxesForSelection(selection); @@ -206,7 +211,7 @@ class SuperSelectableTextState extends State implements Tex @override TextBox getCharacterBox(TextPosition position) { if (_renderParagraph == null) { - return TextBox.fromLTRBD(0, 0, 0, 0, TextDirection.ltr); + return const TextBox.fromLTRBD(0, 0, 0, 0, TextDirection.ltr); } return _renderParagraph! @@ -220,13 +225,15 @@ class SuperSelectableTextState extends State implements Tex @override TextPosition getPositionAtStartOfLine(TextPosition currentPosition) { if (_renderParagraph == null) { - return TextPosition(offset: -1); + return const TextPosition(offset: -1); } final renderParagraph = _renderParagraph!; // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. - final positionOffset = renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, _lineHeight / 2); + final positionOffset = + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + + Offset(0, _lineHeight / 2); final endOfLineOffset = Offset(0, positionOffset.dy); return renderParagraph.getPositionForOffset(endOfLineOffset); } @@ -234,14 +241,17 @@ class SuperSelectableTextState extends State implements Tex @override TextPosition getPositionAtEndOfLine(TextPosition currentPosition) { if (_renderParagraph == null) { - return TextPosition(offset: -1); + return const TextPosition(offset: -1); } final renderParagraph = _renderParagraph!; // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. - final positionOffset = renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, _lineHeight / 2); - final endOfLineOffset = Offset(renderParagraph.size.width, positionOffset.dy); + final positionOffset = + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + + Offset(0, _lineHeight / 2); + final endOfLineOffset = + Offset(renderParagraph.size.width, positionOffset.dy); return renderParagraph.getPositionForOffset(endOfLineOffset); } @@ -256,7 +266,8 @@ class SuperSelectableTextState extends State implements Tex // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. final currentSelectionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, lineHeight / 2); + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + + Offset(0, lineHeight / 2); final oneLineUpOffset = currentSelectionOffset - Offset(0, lineHeight); if (oneLineUpOffset.dy < 0) { @@ -278,7 +289,8 @@ class SuperSelectableTextState extends State implements Tex // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. final currentSelectionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, lineHeight / 2); + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + + Offset(0, lineHeight / 2); final oneLineDownOffset = currentSelectionOffset + Offset(0, lineHeight); if (oneLineDownOffset.dy > renderParagraph.size.height) { @@ -299,7 +311,7 @@ class SuperSelectableTextState extends State implements Tex @override TextPosition getPositionInLastLineAtX(double x) { if (_renderParagraph == null) { - return TextPosition(offset: -1); + return const TextPosition(offset: -1); } return getPositionAtOffset( @@ -309,7 +321,7 @@ class SuperSelectableTextState extends State implements Tex TextSelection getWordSelectionAt(TextPosition position) { if (_renderParagraph == null) { - return TextSelection.collapsed(offset: -1); + return const TextSelection.collapsed(offset: -1); } final wordRange = _renderParagraph!.getWordBoundary(position); @@ -320,7 +332,8 @@ class SuperSelectableTextState extends State implements Tex } @override - TextSelection expandSelection(TextPosition position, TextExpansion expansion, TextAffinity affinity) { + TextSelection expandSelection( + TextPosition position, TextExpansion expansion, TextAffinity affinity) { return expansion(widget.richText.toPlainText(), position, affinity); } @@ -355,7 +368,8 @@ class SuperSelectableTextState extends State implements Tex } final renderParagraph = _renderParagraph!; - final contentOffset = renderParagraph.localToGlobal(Offset.zero, ancestor: ancestorCoordinateSpace); + final contentOffset = renderParagraph.localToGlobal(Offset.zero, + ancestor: ancestorCoordinateSpace); final textRect = contentOffset & renderParagraph.size; if (region.overlaps(textRect)) { @@ -369,7 +383,7 @@ class SuperSelectableTextState extends State implements Tex @override TextSelection getSelectionInRect(Offset baseOffset, Offset extentOffset) { if (_renderParagraph == null) { - return TextSelection.collapsed(offset: -1); + return const TextSelection.collapsed(offset: -1); } final renderParagraph = _renderParagraph!; @@ -427,7 +441,7 @@ class SuperSelectableTextState extends State implements Tex Widget _buildTextSelection() { if (_renderParagraph == null) { - return SizedBox(); + return const SizedBox(); } return widget.textSelectionDecoration.build( @@ -450,7 +464,7 @@ class SuperSelectableTextState extends State implements Tex Widget _buildTextCaret() { if (_renderParagraph == null) { - return SizedBox(); + return const SizedBox(); } return RepaintBoundary( @@ -507,7 +521,7 @@ class _TextSelectionPainter extends CustomPainter { final bool isTextEmpty; final RenderParagraph renderParagraph; final TextSelection selection; - final emptySelectionHeight; + final double emptySelectionHeight; // When true, an empty, collapsed selection will be highlighted // for the purpose of showing a highlighted empty line. final bool highlightWhenEmpty; @@ -516,11 +530,14 @@ class _TextSelectionPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - if (isTextEmpty && highlightWhenEmpty && selection.isCollapsed && selection.extentOffset == 0) { + if (isTextEmpty && + highlightWhenEmpty && + selection.isCollapsed && + selection.extentOffset == 0) { //&& highlightWhenEmpty) { // This is an empty paragraph, which is selected. Paint a small selection. canvas.drawRect( - Rect.fromLTWH(0, 0, 5, 20), + const Rect.fromLTWH(0, 0, 5, 20), selectionPaint, ); } @@ -529,12 +546,15 @@ class _TextSelectionPainter extends CustomPainter { for (final box in selectionBoxes) { final rawRect = box.toRect(); - final rect = Rect.fromLTWH(rawRect.left, rawRect.top - 2, rawRect.width, rawRect.height + 4); + final rect = Rect.fromLTWH( + rawRect.left, rawRect.top - 2, rawRect.width, rawRect.height + 4); canvas.drawRect( // Note: If the rect has no width then we've selected an empty line. Give // that line a slight width for visibility. - rect.width > 0 ? rect : Rect.fromLTWH(rect.left, rect.top, 5, rect.height), + rect.width > 0 + ? rect + : Rect.fromLTWH(rect.left, rect.top, 5, rect.height), selectionPaint, ); } @@ -542,7 +562,8 @@ class _TextSelectionPainter extends CustomPainter { @override bool shouldRepaint(_TextSelectionPainter oldDelegate) { - return renderParagraph != oldDelegate.renderParagraph || selection != oldDelegate.selection; + return renderParagraph != oldDelegate.renderParagraph || + selection != oldDelegate.selection; } } @@ -604,7 +625,8 @@ class _BlinkingCaret extends StatefulWidget { _BlinkingCaretState createState() => _BlinkingCaretState(); } -class _BlinkingCaretState extends State<_BlinkingCaret> with SingleTickerProviderStateMixin { +class _BlinkingCaretState extends State<_BlinkingCaret> + with SingleTickerProviderStateMixin { // Controls the blinking caret animation. late _CaretBlinkController _caretBlinkController; @@ -668,7 +690,8 @@ class _CursorPainter extends CustomPainter { final int caretTextPosition; final double width; final BorderRadius borderRadius; - final double lineHeight; // TODO: this should probably also come from the TextPainter (#46). + final double + lineHeight; // TODO: this should probably also come from the TextPainter (#46). final bool isTextEmpty; final bool showCaret; final Color caretColor; @@ -686,11 +709,14 @@ class _CursorPainter extends CustomPainter { caretPaint.color = caretColor.withOpacity(blinkController.opacity); - final caretHeight = paragraph.getFullHeightForCaret(TextPosition(offset: caretTextPosition)) ?? lineHeight; + final caretHeight = paragraph + .getFullHeightForCaret(TextPosition(offset: caretTextPosition)) ?? + lineHeight; Offset caretOffset = isTextEmpty ? Offset(0, (lineHeight - caretHeight) / 2) - : paragraph.getOffsetForCaret(TextPosition(offset: caretTextPosition), Rect.zero); + : paragraph.getOffsetForCaret( + TextPosition(offset: caretTextPosition), Rect.zero); if (borderRadius == BorderRadius.zero) { canvas.drawRect( @@ -789,14 +815,17 @@ class DebugSelectableTextDecorator extends StatefulWidget { final bool showDebugPaint; @override - _DebugSelectableTextDecoratorState createState() => _DebugSelectableTextDecoratorState(); + _DebugSelectableTextDecoratorState createState() => + _DebugSelectableTextDecoratorState(); } -class _DebugSelectableTextDecoratorState extends State { +class _DebugSelectableTextDecoratorState + extends State { SuperSelectableTextState? get _selectableTextState => widget.selectableTextKey.currentState as SuperSelectableTextState?; - RenderParagraph? get _renderParagraph => _selectableTextState?._renderParagraph; + RenderParagraph? get _renderParagraph => + _selectableTextState?._renderParagraph; List _computeTextRectangles(RenderParagraph renderParagraph) { return renderParagraph @@ -824,21 +853,22 @@ class _DebugSelectableTextDecoratorState extends State { final _selectableTextKey = GlobalKey(); final _textScrollKey = GlobalKey(); late FocusNode _focusNode; - bool _hasFocus = false; // cache whether we have focus so we know when it changes + bool _hasFocus = + false; // cache whether we have focus so we know when it changes late AttributedTextEditingController _controller; late ScrollController _scrollController; @@ -123,7 +124,8 @@ class SuperTextFieldState extends State { if (oldWidget.focusNode == null) { _focusNode.dispose(); } - _focusNode = (widget.focusNode ?? FocusNode())..addListener(_onFocusChange); + _focusNode = (widget.focusNode ?? FocusNode()) + ..addListener(_onFocusChange); _hasFocus = _focusNode.hasFocus; } @@ -164,7 +166,8 @@ class SuperTextFieldState extends State { // // This behavior matches Flutter's standard behavior. if (_focusNode.hasFocus && !_hasFocus) { - _controller.selection = TextSelection.collapsed(offset: _controller.text.text.length); + _controller.selection = + TextSelection.collapsed(offset: _controller.text.text.length); } _hasFocus = _focusNode.hasFocus; } @@ -185,8 +188,12 @@ class SuperTextFieldState extends State { final estimatedLineHeight = _getEstimatedLineHeight(); final estimatedLinesOfText = _getEstimatedLinesOfText(); final estimatedContentHeight = estimatedLinesOfText * estimatedLineHeight; - final minHeight = widget.minLines != null ? widget.minLines! * estimatedLineHeight + widget.padding.vertical : null; - final maxHeight = widget.maxLines != null ? widget.maxLines! * estimatedLineHeight + widget.padding.vertical : null; + final minHeight = widget.minLines != null + ? widget.minLines! * estimatedLineHeight + widget.padding.vertical + : null; + final maxHeight = widget.maxLines != null + ? widget.maxLines! * estimatedLineHeight + widget.padding.vertical + : null; double? viewportHeight; if (maxHeight != null && estimatedContentHeight > maxHeight) { viewportHeight = maxHeight; @@ -215,8 +222,9 @@ class SuperTextFieldState extends State { return 0; } - final offsetAtEndOfText = - _selectableTextKey.currentState!.getOffsetAtPosition(TextPosition(offset: _controller.text.text.length)); + final offsetAtEndOfText = _selectableTextKey.currentState! + .getOffsetAtPosition( + TextPosition(offset: _controller.text.text.length)); int lineCount = (offsetAtEndOfText.dy / _getEstimatedLineHeight()).ceil(); if (_controller.text.text.endsWith('\n')) { @@ -268,8 +276,13 @@ class SuperTextFieldState extends State { builder: (context) { final isTextEmpty = _controller.text.text.isEmpty; final showHint = widget.hintBuilder != null && - ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || - (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); + ((isTextEmpty && + widget.hintBehavior == + HintBehavior.displayHintUntilTextEntered) || + (isTextEmpty && + !_focusNode.hasFocus && + widget.hintBehavior == + HintBehavior.displayHintUntilFocus)); return _buildDecoration( child: SuperTextFieldScrollview( @@ -298,7 +311,9 @@ class SuperTextFieldState extends State { Widget _buildDecoration({ required Widget child, }) { - return widget.decorationBuilder != null ? widget.decorationBuilder!(context, child) : child; + return widget.decorationBuilder != null + ? widget.decorationBuilder!(context, child) + : child; } Widget _buildSelectableText() { @@ -379,10 +394,12 @@ class SuperTextFieldGestureInteractor extends StatefulWidget { final Widget child; @override - _SuperTextFieldGestureInteractorState createState() => _SuperTextFieldGestureInteractorState(); + _SuperTextFieldGestureInteractorState createState() => + _SuperTextFieldGestureInteractorState(); } -class _SuperTextFieldGestureInteractorState extends State { +class _SuperTextFieldGestureInteractorState + extends State { final _cursorStyle = ValueNotifier(SystemMouseCursors.basic); _SelectionType _selectionType = _SelectionType.position; @@ -397,7 +414,8 @@ class _SuperTextFieldGestureInteractorState extends State widget.textKey.currentState!; - SuperTextFieldScrollviewState get _textScroll => widget.textScrollKey.currentState!; + SuperTextFieldScrollviewState get _textScroll => + widget.textScrollKey.currentState!; void _onTapDown(TapDownDetails details) { _log.log('_onTapDown', 'EditableDocument: onTapDown()'); @@ -406,8 +424,10 @@ class _SuperTextFieldGestureInteractorState extends State{ - TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers( + TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers< + TapSequenceGestureRecognizer>( () => TapSequenceGestureRecognizer(), (TapSequenceGestureRecognizer recognizer) { recognizer @@ -730,7 +777,8 @@ class _SuperTextFieldGestureInteractorState extends State( + PanGestureRecognizer: + GestureRecognizerFactoryWithHandlers( () => PanGestureRecognizer(), (PanGestureRecognizer recognizer) { recognizer @@ -810,10 +858,12 @@ class SuperTextFieldKeyboardInteractor extends StatefulWidget { final Widget child; @override - _SuperTextFieldKeyboardInteractorState createState() => _SuperTextFieldKeyboardInteractorState(); + _SuperTextFieldKeyboardInteractorState createState() => + _SuperTextFieldKeyboardInteractorState(); } -class _SuperTextFieldKeyboardInteractorState extends State { +class _SuperTextFieldKeyboardInteractorState + extends State { KeyEventResult _onKeyPressed(FocusNode focusNode, RawKeyEvent keyEvent) { _log.log('_onKeyPressed', 'keyEvent: ${keyEvent.character}'); if (keyEvent is! RawKeyDownEvent) { @@ -821,9 +871,11 @@ class _SuperTextFieldKeyboardInteractorState extends State SuperTextFieldScrollviewState(); + SuperTextFieldScrollviewState createState() => + SuperTextFieldScrollviewState(); } -class SuperTextFieldScrollviewState extends State with SingleTickerProviderStateMixin { +class SuperTextFieldScrollviewState extends State + with SingleTickerProviderStateMixin { bool _scrollToStartOnTick = false; bool _scrollToEndOnTick = false; double _scrollAmountPerFrame = 0; @@ -969,17 +1025,24 @@ class SuperTextFieldScrollviewState extends State with final extentOffset = _text.getOffsetAtPosition(selection.extent); - final gutterExtent = 0; // _dragGutterExtent + const gutterExtent = 0; // _dragGutterExtent final myBox = context.findRenderObject() as RenderBox; - final beyondLeftExtent = min(extentOffset.dx - widget.scrollController.offset - gutterExtent, 0).abs(); + final beyondLeftExtent = + min(extentOffset.dx - widget.scrollController.offset - gutterExtent, 0) + .abs(); final beyondRightExtent = max( - extentOffset.dx - myBox.size.width - widget.scrollController.offset + gutterExtent + widget.padding.horizontal, + extentOffset.dx - + myBox.size.width - + widget.scrollController.offset + + gutterExtent + + widget.padding.horizontal, 0); if (beyondLeftExtent > 0) { - final newScrollPosition = (widget.scrollController.offset - beyondLeftExtent) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = + (widget.scrollController.offset - beyondLeftExtent) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -987,8 +1050,9 @@ class SuperTextFieldScrollviewState extends State with curve: Curves.easeOut, ); } else if (beyondRightExtent > 0) { - final newScrollPosition = (beyondRightExtent + widget.scrollController.offset) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = + (beyondRightExtent + widget.scrollController.offset) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1006,30 +1070,40 @@ class SuperTextFieldScrollviewState extends State with final extentOffset = _text.getOffsetAtPosition(selection.extent); - final gutterExtent = 0; // _dragGutterExtent - final extentLineIndex = (extentOffset.dy / widget.estimatedLineHeight).round(); + const gutterExtent = 0; // _dragGutterExtent + final extentLineIndex = + (extentOffset.dy / widget.estimatedLineHeight).round(); final myBox = context.findRenderObject() as RenderBox; - final beyondTopExtent = min(extentOffset.dy - widget.scrollController.offset - gutterExtent, 0).abs(); + final beyondTopExtent = min( + extentOffset.dy - widget.scrollController.offset - gutterExtent, 0) + .abs(); final beyondBottomExtent = max( ((extentLineIndex + 1) * widget.estimatedLineHeight) - myBox.size.height - widget.scrollController.offset + gutterExtent + - (widget.estimatedLineHeight / 2) + // manual adjustment to avoid line getting half cut off + (widget.estimatedLineHeight / + 2) + // manual adjustment to avoid line getting half cut off widget.padding.vertical / 2, 0); _log.log('_ensureSelectionExtentIsVisible', 'Ensuring extent is visible.'); - _log.log('_ensureSelectionExtentIsVisible', ' - interaction size: ${myBox.size}'); - _log.log('_ensureSelectionExtentIsVisible', ' - scroll extent: ${widget.scrollController.offset}'); - _log.log('_ensureSelectionExtentIsVisible', ' - extent rect: $extentOffset'); - _log.log('_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); - _log.log('_ensureSelectionExtentIsVisible', ' - beyond bottom: $beyondBottomExtent'); + _log.log('_ensureSelectionExtentIsVisible', + ' - interaction size: ${myBox.size}'); + _log.log('_ensureSelectionExtentIsVisible', + ' - scroll extent: ${widget.scrollController.offset}'); + _log.log( + '_ensureSelectionExtentIsVisible', ' - extent rect: $extentOffset'); + _log.log( + '_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); + _log.log('_ensureSelectionExtentIsVisible', + ' - beyond bottom: $beyondBottomExtent'); if (beyondTopExtent > 0) { - final newScrollPosition = (widget.scrollController.offset - beyondTopExtent) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = + (widget.scrollController.offset - beyondTopExtent) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1037,8 +1111,9 @@ class SuperTextFieldScrollviewState extends State with curve: Curves.easeOut, ); } else if (beyondBottomExtent > 0) { - final newScrollPosition = (beyondBottomExtent + widget.scrollController.offset) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = + (beyondBottomExtent + widget.scrollController.offset) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1075,7 +1150,8 @@ class SuperTextFieldScrollviewState extends State with return; } - widget.scrollController.position.jumpTo(widget.scrollController.offset - _scrollAmountPerFrame); + widget.scrollController.position + .jumpTo(widget.scrollController.offset - _scrollAmountPerFrame); } void _startScrollingToEnd({required double amountPerFrame}) { @@ -1101,11 +1177,13 @@ class SuperTextFieldScrollviewState extends State with } void _scrollToEnd() { - if (widget.scrollController.offset >= widget.scrollController.position.maxScrollExtent) { + if (widget.scrollController.offset >= + widget.scrollController.position.maxScrollExtent) { return; } - widget.scrollController.position.jumpTo(widget.scrollController.offset + _scrollAmountPerFrame); + widget.scrollController.position + .jumpTo(widget.scrollController.offset + _scrollAmountPerFrame); } void _onTick(elapsedTime) { @@ -1123,7 +1201,7 @@ class SuperTextFieldScrollviewState extends State with height: widget.viewportHeight, child: SingleChildScrollView( controller: widget.scrollController, - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), scrollDirection: widget.isMultiline ? Axis.vertical : Axis.horizontal, child: Padding( padding: widget.padding, @@ -1134,8 +1212,8 @@ class SuperTextFieldScrollviewState extends State with } } -typedef RightClickListener = void Function( - BuildContext textFieldContext, AttributedTextEditingController textController, Offset textFieldOffset); +typedef RightClickListener = void Function(BuildContext textFieldContext, + AttributedTextEditingController textController, Offset textFieldOffset); enum _SelectionType { /// The selection bound is set on a per-character basis. @@ -1205,8 +1283,10 @@ const defaultTextFieldKeyboardHandlers = [ DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed, DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed, DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys, - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed, - DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed, + DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed, + DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed, DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed, DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed, ]; @@ -1289,7 +1369,8 @@ class DefaultSuperTextFieldKeyboardHandlers { } if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); final movementModifiers = { 'movement_unit': 'character', @@ -1307,7 +1388,8 @@ class DefaultSuperTextFieldKeyboardHandlers { movementModifiers: movementModifiers, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling right arrow key'); + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling right arrow key'); final movementModifiers = { 'movement_unit': 'character', @@ -1325,14 +1407,16 @@ class DefaultSuperTextFieldKeyboardHandlers { movementModifiers: movementModifiers, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowUp) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); controller.moveCaretVertically( selectableTextState: selectableTextState, expandSelection: keyEvent.isShiftPressed, moveUp: true, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowDown) { - _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); + _log.log( + 'moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); controller.moveCaretVertically( selectableTextState: selectableTextState, expandSelection: keyEvent.isShiftPressed, @@ -1376,12 +1460,14 @@ class DefaultSuperTextFieldKeyboardHandlers { return TextFieldKeyboardHandlerResult.handled; } - static TextFieldKeyboardHandlerResult deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed({ + static TextFieldKeyboardHandlerResult + deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed({ required AttributedTextEditingController controller, required SuperSelectableTextState selectableTextState, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.logicalKey != LogicalKeyboardKey.backspace) { + if (!keyEvent.isPrimaryShortcutKeyPressed || + keyEvent.logicalKey != LogicalKeyboardKey.backspace) { return TextFieldKeyboardHandlerResult.notHandled; } if (!controller.selection.isCollapsed) { @@ -1390,17 +1476,21 @@ class DefaultSuperTextFieldKeyboardHandlers { if (controller.selection.extentOffset < 0) { return TextFieldKeyboardHandlerResult.notHandled; } - if (selectableTextState.getPositionAtStartOfLine(controller.selection.extent).offset == + if (selectableTextState + .getPositionAtStartOfLine(controller.selection.extent) + .offset == controller.selection.extentOffset) { return TextFieldKeyboardHandlerResult.notHandled; } - controller.deleteTextOnLineBeforeCaret(selectableTextState: selectableTextState); + controller.deleteTextOnLineBeforeCaret( + selectableTextState: selectableTextState); return TextFieldKeyboardHandlerResult.handled; } - static TextFieldKeyboardHandlerResult deleteTextWhenBackspaceOrDeleteIsPressed({ + static TextFieldKeyboardHandlerResult + deleteTextWhenBackspaceOrDeleteIsPressed({ required AttributedTextEditingController controller, SuperSelectableTextState? selectableTextState, required RawKeyEvent keyEvent, @@ -1415,7 +1505,8 @@ class DefaultSuperTextFieldKeyboardHandlers { } if (controller.selection.isCollapsed) { - controller.deleteCharacter(isBackspace ? TextAffinity.upstream : TextAffinity.downstream); + controller.deleteCharacter( + isBackspace ? TextAffinity.upstream : TextAffinity.downstream); } else { controller.deleteSelectedText(); } @@ -1446,7 +1537,7 @@ class AttributedTextEditingController with ChangeNotifier { AttributedText? text, TextSelection? selection, }) : _text = text ?? AttributedText(), - _selection = selection ?? TextSelection.collapsed(offset: -1); + _selection = selection ?? const TextSelection.collapsed(offset: -1); void updateTextAndSelection({ required AttributedText text, @@ -1468,8 +1559,12 @@ class AttributedTextEditingController with ChangeNotifier { // the end of the new text value if (_selection.end > _text.text.length) { _selection = _selection.copyWith( - baseOffset: _selection.affinity == TextAffinity.downstream ? _selection.baseOffset : _text.text.length, - extentOffset: _selection.affinity == TextAffinity.downstream ? _text.text.length : _selection.extentOffset, + baseOffset: _selection.affinity == TextAffinity.downstream + ? _selection.baseOffset + : _text.text.length, + extentOffset: _selection.affinity == TextAffinity.downstream + ? _text.text.length + : _selection.extentOffset, ); } @@ -1487,7 +1582,8 @@ class AttributedTextEditingController with ChangeNotifier { } bool isSelectionWithinTextBounds(TextSelection selection) { - return selection.start <= text.text.length && selection.end <= text.text.length; + return selection.start <= text.text.length && + selection.end <= text.text.length; } TextSpan buildTextSpan(AttributionStyleBuilder styleBuilder) { @@ -1496,7 +1592,7 @@ class AttributedTextEditingController with ChangeNotifier { void clear() { _text = AttributedText(); - _selection = TextSelection.collapsed(offset: -1); + _selection = const TextSelection.collapsed(offset: -1); } } @@ -1557,13 +1653,18 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { // extent to the left side of the selection. newExtent = selection.start; } else if (movementModifiers['movement_unit'] == 'line') { - newExtent = selectableTextState.getPositionAtStartOfLine(TextPosition(offset: selection.extentOffset)).offset; + newExtent = selectableTextState + .getPositionAtStartOfLine( + TextPosition(offset: selection.extentOffset)) + .offset; } else if (movementModifiers['movement_unit'] == 'word') { final plainText = text.text; newExtent = selection.extentOffset; newExtent -= 1; // we always want to jump at least 1 character. - while (newExtent > 0 && plainText[newExtent - 1] != ' ' && plainText[newExtent - 1] != '\n') { + while (newExtent > 0 && + plainText[newExtent - 1] != ' ' && + plainText[newExtent - 1] != '\n') { newExtent -= 1; } } else { @@ -1581,13 +1682,15 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { // extent to the left side of the selection. newExtent = selection.end; } else if (movementModifiers['movement_unit'] == 'line') { - final endOfLine = selectableTextState.getPositionAtEndOfLine(TextPosition(offset: selection.extentOffset)); + final endOfLine = selectableTextState.getPositionAtEndOfLine( + TextPosition(offset: selection.extentOffset)); final endPosition = TextPosition(offset: text.text.length); final plainText = text.text; // Note: we compare offset values because we don't care if the affinitys are equal - final isAutoWrapLine = endOfLine.offset != endPosition.offset && (plainText[endOfLine.offset] != '\n'); + final isAutoWrapLine = endOfLine.offset != endPosition.offset && + (plainText[endOfLine.offset] != '\n'); // Note: For lines that auto-wrap, moving the cursor to `offset` causes the // cursor to jump to the next line because the cursor is placed after @@ -1608,7 +1711,9 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { newExtent = extentPosition.offset; newExtent += 1; // we always want to jump at least 1 character. - while (newExtent < plainText.length && plainText[newExtent] != ' ' && plainText[newExtent] != '\n') { + while (newExtent < plainText.length && + plainText[newExtent] != ' ' && + plainText[newExtent] != '\n') { newExtent += 1; } } else { @@ -1630,13 +1735,15 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { int? newExtent; if (moveUp) { - newExtent = selectableTextState.getPositionOneLineUp(selection.extent)?.offset; + newExtent = + selectableTextState.getPositionOneLineUp(selection.extent)?.offset; // If there is no line above the current selection, move selection // to the beginning of the available text. newExtent ??= 0; } else { - newExtent = selectableTextState.getPositionOneLineDown(selection.extent)?.offset; + newExtent = + selectableTextState.getPositionOneLineDown(selection.extent)?.offset; // If there is no line below the current selection, move selection // to the end of the available text. @@ -1655,7 +1762,8 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { final existingAttributions = text.getAllAttributionsAt(initialTextOffset); if (!selection.isCollapsed) { - text = text.removeRegion(startOffset: selection.start, endOffset: selection.end); + text = text.removeRegion( + startOffset: selection.start, endOffset: selection.end); selection = TextSelection.collapsed(offset: selection.start); } @@ -1696,7 +1804,8 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { }) { assert(selection.isCollapsed); - final startOfLinePosition = selectableTextState.getPositionAtStartOfLine(selection.extent); + final startOfLinePosition = + selectableTextState.getPositionAtStartOfLine(selection.extent); selection = TextSelection( baseOffset: selection.extentOffset, extentOffset: startOfLinePosition.offset, @@ -1727,6 +1836,7 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { textToInsert: '\n', startOffset: currentSelectionExtent.offset, ); - selection = TextSelection.collapsed(offset: currentSelectionExtent.offset + 1); + selection = + TextSelection.collapsed(offset: currentSelectionExtent.offset + 1); } } diff --git a/super_editor/lib/src/infrastructure/text_layout.dart b/super_editor/lib/src/infrastructure/text_layout.dart index 651947be17..097550021f 100644 --- a/super_editor/lib/src/infrastructure/text_layout.dart +++ b/super_editor/lib/src/infrastructure/text_layout.dart @@ -61,15 +61,19 @@ abstract class TextLayout { /// Returns a [TextSelection] that surrounds the given [startingPosition] and expands /// outward until the given [expansion] chooses to stop expanding. - TextSelection expandSelection(TextPosition startingPosition, TextExpansion expansion, TextAffinity affinity); + TextSelection expandSelection(TextPosition startingPosition, + TextExpansion expansion, TextAffinity affinity); } -typedef TextExpansion = TextSelection Function(String text, TextPosition startingPosition, TextAffinity affinity); +typedef TextExpansion = TextSelection Function( + String text, TextPosition startingPosition, TextAffinity affinity); -TextSelection paragraphExpansionFilter(String text, TextPosition startingPosition, TextAffinity affinity) { +TextSelection paragraphExpansionFilter( + String text, TextPosition startingPosition, TextAffinity affinity) { // If the given position falls directly on a newline then return // just the newline character as the paragraph selection. - if (startingPosition.offset < text.length && text[startingPosition.offset] == '\n') { + if (startingPosition.offset < text.length && + text[startingPosition.offset] == '\n') { return TextSelection.collapsed(offset: startingPosition.offset); } @@ -114,7 +118,10 @@ int getCharacterEndBounds(String text, int startingCodePointIndex) { // TODO: copy the implementation of nextCharacter to this package because // it's marked as visible for testing - final startOffset = RenderEditable.nextCharacter(startingCodePointIndex, text); + // ignore: invalid_use_of_visible_for_testing_member + final startOffset = + // ignore: invalid_use_of_visible_for_testing_member + RenderEditable.nextCharacter(startingCodePointIndex, text); return startOffset; } @@ -138,6 +145,9 @@ int getCharacterStartBounds(String text, int endingCodePointIndex) { // TODO: copy the implementation of previousCharacter to this package because // it's marked as visible for testing - final startOffset = RenderEditable.previousCharacter(endingCodePointIndex, text); + // ignore: invalid_use_of_visible_for_testing_member + final startOffset = + // ignore: invalid_use_of_visible_for_testing_member + RenderEditable.previousCharacter(endingCodePointIndex, text); return startOffset; } diff --git a/super_editor/lib/src/serialization/markdown.dart b/super_editor/lib/src/serialization/markdown.dart index 1987415db8..e60694fca6 100644 --- a/super_editor/lib/src/serialization/markdown.dart +++ b/super_editor/lib/src/serialization/markdown.dart @@ -10,7 +10,7 @@ import 'package:markdown/markdown.dart' as md; // requires one. When the editing system matures, there should // be a way to return something here that is not concrete. MutableDocument deserializeMarkdownToDocument(String markdown) { - final markdownLines = LineSplitter().convert(markdown); + final markdownLines = const LineSplitter().convert(markdown); final markdownDoc = md.Document(); final blockParser = md.BlockParser(markdownLines, markdownDoc); @@ -155,7 +155,8 @@ class _MarkdownToDocument implements md.NodeVisitor { break; case 'li': if (_listItemTypeStack.isEmpty) { - throw Exception('Tried to parse a markdown list item but the list item type was null'); + throw Exception( + 'Tried to parse a markdown list item but the list item type was null'); } _addListItem( @@ -363,7 +364,8 @@ class _InlineMarkdownToDocument implements md.NodeVisitor { @override void visitText(md.Text text) { final attributedText = _textStack.removeLast(); - _textStack.add(attributedText.copyAndAppend(AttributedText(text: text.text))); + _textStack + .add(attributedText.copyAndAppend(AttributedText(text: text.text))); } @override @@ -403,11 +405,18 @@ extension on AttributedText { /// Serializes style attributions into markdown syntax in a repeatable /// order such that opening and closing styles match each other on /// the opening and closing ends of a span. - static String _sortAndSerializeAttributions(Set attributions, AttributionVisitEvent event) { - const startOrder = [codeAttribution, boldAttribution, italicsAttribution, strikethroughAttribution]; + static String _sortAndSerializeAttributions( + Set attributions, AttributionVisitEvent event) { + const startOrder = [ + codeAttribution, + boldAttribution, + italicsAttribution, + strikethroughAttribution + ]; final buffer = StringBuffer(); - final encodingOrder = event == AttributionVisitEvent.start ? startOrder : startOrder.reversed; + final encodingOrder = + event == AttributionVisitEvent.start ? startOrder : startOrder.reversed; for (final markdownStyleAttribution in encodingOrder) { if (attributions.contains(markdownStyleAttribution)) { @@ -447,7 +456,9 @@ extension on AttributedText { case AttributionVisitEvent.end: // +1 on end index because this visitor has inclusive indices // whereas substring() expects an exclusive ending index. - buffer..write(fullText.text.substring(spanStart, index + 1))..write(markdownStyles); + buffer + ..write(fullText.text.substring(spanStart, index + 1)) + ..write(markdownStyles); break; } }); diff --git a/super_editor/pubspec.lock b/super_editor/pubspec.lock index e19f0e00a6..fc320fb00e 100644 --- a/super_editor/pubspec.lock +++ b/super_editor/pubspec.lock @@ -93,7 +93,7 @@ packages: source: hosted version: "3.7.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection url: "https://pub.dartlang.org" @@ -146,6 +146,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_lints: + dependency: "direct main" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -179,6 +186,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" logging: dependency: transitive description: @@ -229,7 +243,7 @@ packages: source: hosted version: "1.8.0" pedantic: - dependency: "direct main" + dependency: transitive description: name: pedantic url: "https://pub.dartlang.org" diff --git a/super_editor/pubspec.yaml b/super_editor/pubspec.yaml index 480d76c77a..2e807da6f6 100644 --- a/super_editor/pubspec.yaml +++ b/super_editor/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: # TODO: move markdown serialization to a separate package and # then remove this dependency. markdown: ^4.0.0 - pedantic: ^1.11.0 + flutter_lints: ^1.0.0 uuid: ^3.0.3 dev_dependencies: diff --git a/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart b/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart index 374170014e..ff3fe8e93e 100644 --- a/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart +++ b/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart @@ -35,7 +35,8 @@ void main() { ); final composer = DocumentComposer(); final layoutKey = GlobalKey(); - final documentLayoutResolver = () => layoutKey.currentState as DocumentLayout; + DocumentLayout documentLayoutResolver() => + layoutKey.currentState as DocumentLayout; final commonOps = CommonEditorOperations( editor: documentEditor, composer: composer, @@ -58,17 +59,17 @@ void main() { composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: documentEditor.document.nodes.first.id, - nodePosition: TextNodePosition(offset: 0), + nodePosition: const TextNodePosition(offset: 0), )); - final header = 'Smoke Test'; + const header = 'Smoke Test'; for (final character in header.characters) { commonOps.insertCharacter(character); } commonOps.insertBlockLevelNewline(); - final p1 = + const p1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sed sagittis urna. Aenean mattis ante justo, quis sollicitudin metus interdum id.'; for (final character in p1.characters) { commonOps.insertCharacter(character); @@ -91,7 +92,8 @@ void main() { // print(' - $node'); // } - expect(documentEditor.document.hasEquivalentContent(expectedDocument), true); + expect( + documentEditor.document.hasEquivalentContent(expectedDocument), true); }); }); } diff --git a/super_editor/test/serialization/markdown_test.dart b/super_editor/test/serialization/markdown_test.dart index 8d3d6ec918..4bd26effde 100644 --- a/super_editor/test/serialization/markdown_test.dart +++ b/super_editor/test/serialization/markdown_test.dart @@ -15,22 +15,28 @@ void main() { ), ]); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header1Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header1Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '# My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header2Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header2Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '## My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header3Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header3Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header4Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header4Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '#### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header5Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header5Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '##### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header6Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = + header6Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '###### My Header'); }); @@ -42,8 +48,14 @@ void main() { text: 'My Header', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 3, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 3, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 8, + markerType: SpanMarkerType.end), ], ), ), @@ -63,7 +75,8 @@ void main() { ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), '> This is a blockquote'); + expect( + serializeDocumentToMarkdown(doc).trim(), '> This is a blockquote'); }); test('blockquote with styles', () { @@ -74,8 +87,14 @@ void main() { text: 'This is a blockquote', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 10, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 19, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 10, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 19, + markerType: SpanMarkerType.end), ], ), ), @@ -83,7 +102,8 @@ void main() { ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), '> This is a **blockquote**'); + expect(serializeDocumentToMarkdown(doc).trim(), + '> This is a **blockquote**'); }); test('code', () { @@ -123,15 +143,22 @@ This is some code text: 'This is a paragraph.', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 5, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 8, + markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), 'This **is a** paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), + 'This **is a** paragraph.'); }); test('paragraph with overlapping bold and italics', () { @@ -142,17 +169,30 @@ This is some code text: 'This is a paragraph.', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), - SpanMarker(attribution: italicsAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: italicsAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 5, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 8, + markerType: SpanMarkerType.end), + const SpanMarker( + attribution: italicsAttribution, + offset: 5, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: italicsAttribution, + offset: 8, + markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), 'This ***is a*** paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), + 'This ***is a*** paragraph.'); }); test('paragraph with overlapping code and bold', () { @@ -162,18 +202,31 @@ This is some code text: AttributedText( text: 'This is a paragraph.', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), - SpanMarker(attribution: codeAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: codeAttribution, offset: 8, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: boldAttribution, + offset: 5, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: boldAttribution, + offset: 8, + markerType: SpanMarkerType.end), + SpanMarker( + attribution: codeAttribution, + offset: 5, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: codeAttribution, + offset: 8, + markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), 'This `**is a**` paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), + 'This `**is a**` paragraph.'); }); test('image', () { @@ -185,7 +238,8 @@ This is some code ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), '![some alt text](https://someimage.com/the/image.png)'); + expect(serializeDocumentToMarkdown(doc).trim(), + '![some alt text](https://someimage.com/the/image.png)'); }); test('horizontal rule', () { @@ -249,8 +303,14 @@ This is some code text: 'Unordered 1', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 0, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 8, + markerType: SpanMarkerType.end), ], ), ), @@ -311,8 +371,14 @@ This is some code text: 'Ordered 1', spans: AttributedSpans( attributions: [ - SpanMarker(attribution: boldAttribution, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 6, markerType: SpanMarkerType.end), + const SpanMarker( + attribution: boldAttribution, + offset: 0, + markerType: SpanMarkerType.start), + const SpanMarker( + attribution: boldAttribution, + offset: 6, + markerType: SpanMarkerType.end), ], ), ), @@ -394,26 +460,33 @@ This is some code group('deserialization', () { test('headers', () { final header1Doc = deserializeMarkdownToDocument('# Header 1'); - expect((header1Doc.nodes.first as ParagraphNode).metadata['blockType'], header1Attribution); + expect((header1Doc.nodes.first as ParagraphNode).metadata['blockType'], + header1Attribution); final header2Doc = deserializeMarkdownToDocument('## Header 2'); - expect((header2Doc.nodes.first as ParagraphNode).metadata['blockType'], header2Attribution); + expect((header2Doc.nodes.first as ParagraphNode).metadata['blockType'], + header2Attribution); final header3Doc = deserializeMarkdownToDocument('### Header 3'); - expect((header3Doc.nodes.first as ParagraphNode).metadata['blockType'], header3Attribution); + expect((header3Doc.nodes.first as ParagraphNode).metadata['blockType'], + header3Attribution); final header4Doc = deserializeMarkdownToDocument('#### Header 4'); - expect((header4Doc.nodes.first as ParagraphNode).metadata['blockType'], header4Attribution); + expect((header4Doc.nodes.first as ParagraphNode).metadata['blockType'], + header4Attribution); final header5Doc = deserializeMarkdownToDocument('##### Header 5'); - expect((header5Doc.nodes.first as ParagraphNode).metadata['blockType'], header5Attribution); + expect((header5Doc.nodes.first as ParagraphNode).metadata['blockType'], + header5Attribution); final header6Doc = deserializeMarkdownToDocument('###### Header 6'); - expect((header6Doc.nodes.first as ParagraphNode).metadata['blockType'], header6Attribution); + expect((header6Doc.nodes.first as ParagraphNode).metadata['blockType'], + header6Attribution); }); test('blockquote', () { - final blockquoteDoc = deserializeMarkdownToDocument('> This is a blockquote'); + final blockquoteDoc = + deserializeMarkdownToDocument('> This is a blockquote'); final blockquote = blockquoteDoc.nodes.first as ParagraphNode; expect(blockquote.metadata['blockType'], blockquoteAttribution); @@ -432,7 +505,8 @@ This is some code }); test('image', () { - final codeBlockDoc = deserializeMarkdownToDocument('![Image alt text](https://images.com/some/image.png)'); + final codeBlockDoc = deserializeMarkdownToDocument( + '![Image alt text](https://images.com/some/image.png)'); final image = codeBlockDoc.nodes.first as ImageNode; expect(image.imageUrl, 'https://images.com/some/image.png'); @@ -440,7 +514,7 @@ This is some code }); test('single unstyled paragraph', () { - final markdown = 'This is some unstyled text to parse as markdown'; + const markdown = 'This is some unstyled text to parse as markdown'; final document = deserializeMarkdownToDocument(markdown); @@ -448,11 +522,12 @@ This is some code expect(document.nodes.first, isA()); final paragraph = document.nodes.first as ParagraphNode; - expect(paragraph.text.text, 'This is some unstyled text to parse as markdown'); + expect(paragraph.text.text, + 'This is some unstyled text to parse as markdown'); }); test('single styled paragraph', () { - final markdown = 'This is **some *styled*** text to parse as markdown'; + const markdown = 'This is **some *styled*** text to parse as markdown'; final document = deserializeMarkdownToDocument(markdown); @@ -461,16 +536,22 @@ This is some code final paragraph = document.nodes.first as ParagraphNode; final styledText = paragraph.text; - expect(styledText.text, 'This is some styled text to parse as markdown'); + expect( + styledText.text, 'This is some styled text to parse as markdown'); expect(styledText.getAllAttributionsAt(0).isEmpty, true); - expect(styledText.getAllAttributionsAt(8).contains(boldAttribution), true); - expect(styledText.getAllAttributionsAt(13).containsAll([boldAttribution, italicsAttribution]), true); + expect( + styledText.getAllAttributionsAt(8).contains(boldAttribution), true); + expect( + styledText + .getAllAttributionsAt(13) + .containsAll([boldAttribution, italicsAttribution]), + true); expect(styledText.getAllAttributionsAt(19).isEmpty, true); }); test('unordered list', () { - final markdown = ''' + const markdown = ''' * list item 1 * list item 2 * list item 2.1 @@ -493,7 +574,7 @@ This is some code }); test('ordered list', () { - final markdown = ''' + const markdown = ''' 1. list item 1 1. list item 2 1. list item 2.1 @@ -521,7 +602,8 @@ This is some code expect(document.nodes.length, 18); expect(document.nodes[0], isA()); - expect((document.nodes[0] as ParagraphNode).metadata['blockType'], header1Attribution); + expect((document.nodes[0] as ParagraphNode).metadata['blockType'], + header1Attribution); expect(document.nodes[1], isA()); @@ -549,7 +631,7 @@ This is some code }); } -final exampleMarkdownDoc1 = ''' +const exampleMarkdownDoc1 = ''' # Example 1 --- This is an example doc that has various types of nodes. diff --git a/super_editor/test/src/_document_test_tools.dart b/super_editor/test/src/_document_test_tools.dart index f77eb51c3c..569c19c1b9 100644 --- a/super_editor/test/src/_document_test_tools.dart +++ b/super_editor/test/src/_document_test_tools.dart @@ -15,7 +15,7 @@ EditContext createEditContext({ CommonEditorOperations? commonOps, }) { final editor = documentEditor ?? DocumentEditor(document: document); - final layoutResolver = () => documentLayout ?? FakeDocumentLayout(); + DocumentLayout layoutResolver() => documentLayout ?? FakeDocumentLayout(); final composer = documentComposer ?? DocumentComposer(); return EditContext( diff --git a/super_editor/test/src/default_editor/attributions_test.dart b/super_editor/test/src/default_editor/attributions_test.dart index cecc44121d..4e2c76754f 100644 --- a/super_editor/test/src/default_editor/attributions_test.dart +++ b/super_editor/test/src/default_editor/attributions_test.dart @@ -16,7 +16,7 @@ void main() { // Add link across "one two" text.addAttribution( LinkAttribution(url: Uri.parse('https://flutter.dev')), - TextRange(start: 0, end: 6), + const TextRange(start: 0, end: 6), ); // Try to add a different link across "two three" and expect @@ -24,7 +24,7 @@ void main() { expect(() { text.addAttribution( LinkAttribution(url: Uri.parse('https://pub.dev')), - TextRange(start: 4, end: 12), + const TextRange(start: 4, end: 12), ); }, throwsA(isA())); }); @@ -34,20 +34,24 @@ void main() { text: 'one two three', ); - final linkAttribution = LinkAttribution(url: Uri.parse('https://flutter.dev')); + final linkAttribution = + LinkAttribution(url: Uri.parse('https://flutter.dev')); // Add link across "one two" text.addAttribution( linkAttribution, - TextRange(start: 0, end: 6), + const TextRange(start: 0, end: 6), ); text.addAttribution( LinkAttribution(url: Uri.parse('https://flutter.dev')), - TextRange(start: 4, end: 12), + const TextRange(start: 4, end: 12), ); - expect(text.spans.hasAttributionsWithin(attributions: {linkAttribution}, start: 0, end: 12), true); + expect( + text.spans.hasAttributionsWithin( + attributions: {linkAttribution}, start: 0, end: 12), + true); }); }); }); diff --git a/super_editor/test/src/default_editor/document_keyboard_actions_test.dart b/super_editor/test/src/default_editor/document_keyboard_actions_test.dart index e22eb4c95d..b709a98e80 100644 --- a/super_editor/test/src/default_editor/document_keyboard_actions_test.dart +++ b/super_editor/test/src/default_editor/document_keyboard_actions_test.dart @@ -27,10 +27,11 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = createEditContext(document: MutableDocument()); + final _editContext = + createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.keyC, @@ -53,10 +54,11 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = createEditContext(document: MutableDocument()); + final _editContext = + createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -79,10 +81,11 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = createEditContext(document: MutableDocument()); + final _editContext = + createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.keyA, @@ -116,7 +119,7 @@ void main() { ); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.keyA, @@ -130,16 +133,17 @@ void main() { expect(result, ExecutionInstruction.haltExecution); expect( _editContext.composer.selection!.base, - DocumentPosition( + const DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 0), ), ); expect( _editContext.composer.selection!.extent, - DocumentPosition( + const DocumentPosition( nodeId: 'paragraph', - nodePosition: TextNodePosition(offset: 'This is some text'.length), + nodePosition: + TextNodePosition(offset: 'This is some text'.length), ), ); @@ -167,7 +171,7 @@ void main() { ); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.keyA, @@ -181,16 +185,17 @@ void main() { expect(result, ExecutionInstruction.haltExecution); expect( _editContext.composer.selection!.base, - DocumentPosition( + const DocumentPosition( nodeId: 'paragraph_1', nodePosition: TextNodePosition(offset: 0), ), ); expect( _editContext.composer.selection!.extent, - DocumentPosition( + const DocumentPosition( nodeId: 'paragraph_2', - nodePosition: TextNodePosition(offset: 'This is some text'.length), + nodePosition: + TextNodePosition(offset: 'This is some text'.length), ), ); @@ -222,7 +227,7 @@ void main() { ); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.keyA, @@ -236,14 +241,14 @@ void main() { expect(result, ExecutionInstruction.haltExecution); expect( _editContext.composer.selection!.base, - DocumentPosition( + const DocumentPosition( nodeId: 'image_1', nodePosition: BinaryNodePosition.included(), ), ); expect( _editContext.composer.selection!.extent, - DocumentPosition( + const DocumentPosition( nodeId: 'image_2', nodePosition: BinaryNodePosition.included(), ), diff --git a/super_editor/test/src/default_editor/text_test.dart b/super_editor/test/src/default_editor/text_test.dart index fa2060d301..7d149240fd 100644 --- a/super_editor/test/src/default_editor/text_test.dart +++ b/super_editor/test/src/default_editor/text_test.dart @@ -33,11 +33,11 @@ void main() { final command = ToggleTextAttributionsCommand( documentSelection: DocumentSelection( - base: DocumentPosition( + base: const DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 1), ), - extent: DocumentPosition( + extent: const DocumentPosition( nodeId: 'paragraph', // IMPORTANT: we want to end the bold at the 'd' character but // the TextPosition indexes the ' ' after the 'd'. This is because @@ -67,7 +67,7 @@ void main() { // Press just the meta key. var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.meta, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -83,7 +83,7 @@ void main() { // Press "a" + meta key result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -103,7 +103,7 @@ void main() { // Try to type a character. var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -128,11 +128,11 @@ void main() { // Select multiple characters in the paragraph editContext.composer.selection = DocumentSelection( - base: DocumentPosition( + base: const DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 0), ), - extent: DocumentPosition( + extent: const DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 1), ), @@ -141,7 +141,7 @@ void main() { // Try to type a character. var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -162,7 +162,7 @@ void main() { ); // Select the horizontal rule node. - editContext.composer.selection = DocumentSelection.collapsed( + editContext.composer.selection = const DocumentSelection.collapsed( position: DocumentPosition( nodeId: 'horizontal_rule', nodePosition: BinaryNodePosition.notIncluded(), @@ -172,7 +172,7 @@ void main() { // Try to type a character. var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -196,7 +196,7 @@ void main() { ); // Select multiple characters in the paragraph - editContext.composer.selection = DocumentSelection.collapsed( + editContext.composer.selection = const DocumentSelection.collapsed( position: DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 0), @@ -206,7 +206,7 @@ void main() { // Press the "alt" key var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( character: null, data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.alt, @@ -222,8 +222,9 @@ void main() { // Press the "enter" key result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( - character: '', // Empirically, pressing enter sends '' as the character instead of null + keyEvent: const FakeRawKeyEvent( + character: + '', // Empirically, pressing enter sends '' as the character instead of null data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.enter, physicalKey: PhysicalKeyboardKey.enter, @@ -247,7 +248,7 @@ void main() { ); // Select multiple characters in the paragraph - editContext.composer.selection = DocumentSelection.collapsed( + editContext.composer.selection = const DocumentSelection.collapsed( position: DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 0), @@ -257,7 +258,7 @@ void main() { // Press the "a" key var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( character: 'a', data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, @@ -286,7 +287,7 @@ void main() { ); // Select multiple characters in the paragraph - editContext.composer.selection = DocumentSelection.collapsed( + editContext.composer.selection = const DocumentSelection.collapsed( position: DocumentPosition( nodeId: 'paragraph', nodePosition: TextNodePosition(offset: 0), @@ -296,7 +297,7 @@ void main() { // Type a non-English character var result = anyCharacterToInsertInTextContent( editContext: editContext, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( character: 'ß', data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, diff --git a/super_editor/test/src/infrastructure/attributed_spans_test.dart b/super_editor/test/src/infrastructure/attributed_spans_test.dart index 7935d337b7..7842e7603e 100644 --- a/super_editor/test/src/infrastructure/attributed_spans_test.dart +++ b/super_editor/test/src/infrastructure/attributed_spans_test.dart @@ -2,21 +2,23 @@ import 'package:super_editor/src/infrastructure/attributed_spans.dart'; import 'package:flutter_test/flutter_test.dart'; // Attributions used throughout this test suite. -final bold = NamedAttribution('bold'); -final italics = NamedAttribution('italics'); -final strikethrough = NamedAttribution('strikethrough'); +const bold = NamedAttribution('bold'); +const italics = NamedAttribution('italics'); +const strikethrough = NamedAttribution('strikethrough'); void main() { group('Spans', () { group('attribution queries', () { test('it expands a span from a given offset', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 3, end: 16); - final expandedSpan = spans.expandAttributionToSpan(attribution: bold, offset: 6); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 3, end: 16); + final expandedSpan = + spans.expandAttributionToSpan(attribution: bold, offset: 6); expect( expandedSpan, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 3, end: 16, @@ -39,7 +41,7 @@ void main() { expect( attributionSpans.first, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 5, end: 10, @@ -62,7 +64,7 @@ void main() { expect( attributionSpans.first, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 3, end: 7, @@ -72,7 +74,7 @@ void main() { expect( attributionSpans.last, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 10, end: 15, @@ -82,7 +84,8 @@ void main() { }); test('it returns spans that completely cover the range', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 10); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 0, end: 10); final attributionSpans = spans.getAttributionSpansInRange( attributionFilter: (attribution) => attribution == bold, start: 3, @@ -93,7 +96,7 @@ void main() { expect( attributionSpans.first, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 0, end: 10, @@ -117,7 +120,7 @@ void main() { expect( attributionSpans.first, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 5, end: 7, @@ -127,7 +130,7 @@ void main() { expect( attributionSpans.last, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 10, end: 12, @@ -137,7 +140,8 @@ void main() { }); test('it resizes spans that completely cover the range', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 10); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 0, end: 10); final attributionSpans = spans.getAttributionSpansInRange( attributionFilter: (attribution) => attribution == bold, start: 3, @@ -149,7 +153,7 @@ void main() { expect( attributionSpans.first, equals( - AttributionSpan( + const AttributionSpan( attribution: bold, start: 3, end: 8, @@ -161,114 +165,165 @@ void main() { group('single attribution', () { test('applies attribution to full span', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 16); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 0, end: 16); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), true); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), + true); }); test('applies attribution to beginning of span', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 7); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 0, end: 7); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 7), true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 7), + true); }); test('applies attribution to inner span', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 2, end: 7); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 2, end: 7); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), + true); }); test('applies attribution to end of span', () { - final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 7, end: 16); + final spans = AttributedSpans() + ..addAttribution(newAttribution: bold, start: 7, end: 16); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 7, end: 16), true); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 7, end: 16), + true); }); test('applies exotic span', () { final linkAttribution = _LinkAttribution( url: 'https://youtube.com/c/superdeclarative', ); - final spans = AttributedSpans()..addAttribution(newAttribution: linkAttribution, start: 2, end: 7); + final spans = AttributedSpans() + ..addAttribution(newAttribution: linkAttribution, start: 2, end: 7); - expect(spans.hasAttributionsWithin(attributions: {linkAttribution}, start: 2, end: 7), true); + expect( + spans.hasAttributionsWithin( + attributions: {linkAttribution}, start: 2, end: 7), + true); }); test('removes attribution from full span', () { final spans = AttributedSpans( attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) + const SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker( + attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 0, end: 16); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), false); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), + false); }); test('removes attribution from inner text span', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 7); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), false); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), + false); }); test('removes attribution from partial beginning span', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 4); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 5, end: 7), true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 5, end: 7), + true); }); test('removes attribution from partial inner span', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 4, end: 5); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 3), true); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 6, end: 7), true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 3), + true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 6, end: 7), + true); }); test('removes attribution from partial ending span', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 5, end: 7); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 4), true); + expect( + spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 4), + true); }); test('applies attribution when mixed span is toggled', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 8, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), true); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), + true); }); test('removes attribution when contiguous span is toggled', () { final spans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), false); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), + false); }); }); @@ -372,7 +427,10 @@ void main() { end: 12, ); - expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 12), true); + expect( + spans + .hasAttributionsWithin(attributions: {bold}, start: 0, end: 12), + true); }); }); @@ -385,9 +443,11 @@ void main() { test('single continuous attribution', () { final collapsedSpans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 16, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -400,11 +460,15 @@ void main() { test('single fractured attribution', () { final collapsedSpans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 10, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 3, markerType: SpanMarkerType.end), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 10, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -427,11 +491,19 @@ void main() { test('multiple non-overlapping attributions', () { final collapsedSpans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 10, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 3, markerType: SpanMarkerType.end), + SpanMarker( + attribution: italics, + offset: 7, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: italics, + offset: 10, + markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -454,11 +526,19 @@ void main() { test('multiple overlapping attributions', () { final collapsedSpans = AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 6, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 16, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 8, markerType: SpanMarkerType.end), + SpanMarker( + attribution: italics, + offset: 6, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: italics, + offset: 16, + markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -509,10 +589,15 @@ class _ExpectedSpans { List _combinedSpans; void expectSpans(AttributedSpans spans) { - for (int characterIndex = 0; characterIndex < _combinedSpans.length; ++characterIndex) { - for (int attributionIndex = 0; attributionIndex < _combinedSpans[characterIndex].length; ++attributionIndex) { + for (int characterIndex = 0; + characterIndex < _combinedSpans.length; + ++characterIndex) { + for (int attributionIndex = 0; + attributionIndex < _combinedSpans[characterIndex].length; + ++attributionIndex) { // The attribution name is just a letter, like 'b', 'i', or 's'. - final attributionName = _combinedSpans[characterIndex][attributionIndex]; + final attributionName = + _combinedSpans[characterIndex][attributionIndex]; if (attributionName == '_') { continue; } @@ -529,10 +614,14 @@ class _ExpectedSpans { namedAttribution = strikethrough; break; default: - throw Exception('Unknown span template character: $attributionName'); + throw Exception( + 'Unknown span template character: $attributionName'); } - expect(spans.hasAttributionAt(characterIndex, attribution: namedAttribution), true); + expect( + spans.hasAttributionAt(characterIndex, + attribution: namedAttribution), + true); } } } @@ -555,7 +644,10 @@ class _LinkAttribution implements Attribution { @override bool operator ==(Object other) => - identical(this, other) || other is _LinkAttribution && runtimeType == other.runtimeType && url == other.url; + identical(this, other) || + other is _LinkAttribution && + runtimeType == other.runtimeType && + url == other.url; @override int get hashCode => url.hashCode; diff --git a/super_editor/test/src/infrastructure/attributed_text_test.dart b/super_editor/test/src/infrastructure/attributed_text_test.dart index 7613819724..50bb6a4325 100644 --- a/super_editor/test/src/infrastructure/attributed_text_test.dart +++ b/super_editor/test/src/infrastructure/attributed_text_test.dart @@ -4,9 +4,9 @@ import 'package:super_editor/src/infrastructure/attributed_text.dart'; import 'package:flutter_test/flutter_test.dart'; // Attributions used throughout this test suite. -final bold = NamedAttribution('bold'); -final italics = NamedAttribution('italics'); -final strikethrough = NamedAttribution('strikethrough'); +const bold = NamedAttribution('bold'); +const italics = NamedAttribution('italics'); +const strikethrough = NamedAttribution('strikethrough'); void main() { group('Attributed Text', () { @@ -24,9 +24,11 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -41,9 +43,11 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 1, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 1, markerType: SpanMarkerType.end), ], ), ); @@ -62,12 +66,14 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ + attributions: const [ // Notice that the markers are provided in reverse order: // end then start. Order shouldn't matter within a single // position index. This test ensures that. - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 1, markerType: SpanMarkerType.end), + SpanMarker( + attribution: bold, offset: 1, markerType: SpanMarkerType.start), ], ), ); @@ -84,7 +90,7 @@ void main() { test('add single character style', () { final text = AttributedText(text: 'abcdefghij'); - text.addAttribution(bold, TextRange(start: 1, end: 1)); + text.addAttribution(bold, const TextRange(start: 1, end: 1)); final textSpan = text.computeTextSpan(_styleBuilder); expect(textSpan.text, null); @@ -100,9 +106,11 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 7, markerType: SpanMarkerType.end), ], ), ); @@ -120,9 +128,11 @@ void main() { final initialText = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 9, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -130,9 +140,11 @@ void main() { final newText = initialText.copyAndAppend(AttributedText( text: 'k', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.end), ], ), )); @@ -155,9 +167,11 @@ void main() { final initialText = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -170,7 +184,8 @@ void main() { expect(newText.text, 'aabcdefghij'); expect( - newText.hasAttributionsWithin(attributions: {bold}, range: TextRange(start: 0, end: 10)), + newText.hasAttributionsWithin( + attributions: {bold}, range: const TextRange(start: 0, end: 10)), true, ); }); @@ -179,11 +194,19 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 4, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 9, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 4, markerType: SpanMarkerType.end), + SpanMarker( + attribution: italics, + offset: 5, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: italics, + offset: 9, + markerType: SpanMarkerType.end), ], ), ); @@ -203,11 +226,19 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 4, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 5, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.end), + attributions: const [ + SpanMarker( + attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker( + attribution: italics, + offset: 4, + markerType: SpanMarkerType.start), + SpanMarker( + attribution: bold, offset: 5, markerType: SpanMarkerType.end), + SpanMarker( + attribution: italics, + offset: 7, + markerType: SpanMarkerType.end), ], ), ); @@ -244,7 +275,7 @@ void main() { listenerCalled = true; }); - text.addAttribution(bold, TextRange(start: 1, end: 1)); + text.addAttribution(bold, const TextRange(start: 1, end: 1)); expect(listenerCalled, isTrue); }); diff --git a/super_editor/test/src/infrastructure/super_textfield_test.dart b/super_editor/test/src/infrastructure/super_textfield_test.dart index deb7f1c03f..4d15bb1bea 100644 --- a/super_editor/test/src/infrastructure/super_textfield_test.dart +++ b/super_editor/test/src/infrastructure/super_textfield_test.dart @@ -22,7 +22,7 @@ void main() { // Note: this is a widget test because we access the Clipboard. final controller = AttributedTextEditingController( text: AttributedText(text: 'This is some text'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 5, extentOffset: 12, ), @@ -38,9 +38,10 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -61,9 +62,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -83,9 +85,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -104,9 +107,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -129,7 +133,7 @@ void main() { // Note: this is a widget test because we access the Clipboard. final controller = AttributedTextEditingController( text: AttributedText(text: 'This is some text'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 5, extentOffset: 12, ), @@ -145,9 +149,10 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -168,9 +173,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -190,9 +196,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyC, physicalKey: PhysicalKeyboardKey.keyC, @@ -211,9 +218,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -238,7 +246,7 @@ void main() { // Note: this is a widget test because we access the Clipboard. final controller = AttributedTextEditingController( text: AttributedText(text: 'Pasted content: '), - selection: TextSelection.collapsed(offset: 16), + selection: const TextSelection.collapsed(offset: 16), ); // The Clipboard requires a platform response, which doesn't exist @@ -252,9 +260,10 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -269,7 +278,8 @@ void main() { WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { // We have to run these expectations in the next frame // so that the async paste operation has time to complete. - expect(controller.text.text, 'Pasted content: this is clipboard text'); + expect(controller.text.text, + 'Pasted content: this is clipboard text'); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 38); }); @@ -282,9 +292,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -304,9 +315,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -326,9 +338,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -351,7 +364,7 @@ void main() { // Note: this is a widget test because we access the Clipboard. final controller = AttributedTextEditingController( text: AttributedText(text: 'Pasted content: '), - selection: TextSelection.collapsed(offset: 16), + selection: const TextSelection.collapsed(offset: 16), ); // The Clipboard requires a platform response, which doesn't exist @@ -365,9 +378,10 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -382,7 +396,8 @@ void main() { WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { // We have to run these expectations in the next frame // so that the async paste operation has time to complete. - expect(controller.text.text, 'Pasted content: this is clipboard text'); + expect(controller.text.text, + 'Pasted content: this is clipboard text'); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 38); }); @@ -395,9 +410,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -417,9 +433,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyV, physicalKey: PhysicalKeyboardKey.keyV, @@ -439,9 +456,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .pasteTextWhenCmdVIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -465,12 +483,13 @@ void main() { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 5), + selection: const TextSelection.collapsed(offset: 5), ); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -492,9 +511,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -514,9 +534,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -536,9 +557,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -560,12 +582,13 @@ void main() { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 5), + selection: const TextSelection.collapsed(offset: 5), ); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -587,9 +610,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -609,9 +633,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyA, physicalKey: PhysicalKeyboardKey.keyA, @@ -631,9 +656,10 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .selectAllTextFieldWhenCmdAIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.metaLeft, physicalKey: PhysicalKeyboardKey.metaLeft, @@ -652,19 +678,22 @@ void main() { group('move caret when arrow key is pressed', () { group('left arrow', () { - testWidgets('it does nothing at beginning of text blob', (tester) async { + testWidgets('it does nothing at beginning of text blob', + (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); // Move by character - final characterResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final characterResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -678,10 +707,11 @@ void main() { expect(controller.selection.extentOffset, 0); // Move by word - final wordResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final wordResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -696,10 +726,11 @@ void main() { expect(controller.selection.extentOffset, 0); // Move to end of line - final lineResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final lineResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -717,15 +748,17 @@ void main() { testWidgets('it moves left by character', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -744,15 +777,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 18), + selection: const TextSelection.collapsed(offset: 18), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -775,22 +809,32 @@ void main() { // We give a tiny bit of wiggle room on the value because when this test // is run on Windows and Linux CI, there is some kind of precision error // that results in a tiny positive number instead of zero. - expect(selectableTextState.getCharacterBox(TextPosition(offset: 16)).top, lessThan(0.1)); - expect(selectableTextState.getCharacterBox(TextPosition(offset: 16)).top, greaterThanOrEqualTo(0)); + expect( + selectableTextState + .getCharacterBox(const TextPosition(offset: 16)) + .top, + lessThan(0.1)); + expect( + selectableTextState + .getCharacterBox(const TextPosition(offset: 16)) + .top, + greaterThanOrEqualTo(0)); }); testWidgets('it expands left by character', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -809,15 +853,17 @@ void main() { testWidgets('it moves left by word', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -835,15 +881,17 @@ void main() { testWidgets('it expands left by word', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -860,20 +908,23 @@ void main() { expect(controller.selection.extentOffset, 6); }); - testWidgets('Mac: cmd+left moves left to beginning of line', (tester) async { + testWidgets('Mac: cmd+left moves left to beginning of line', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -890,20 +941,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Windows + Linux: control + left moves left to beginning of line', (tester) async { + testWidgets( + 'Windows + Linux: control + left moves left to beginning of line', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -920,20 +975,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Mac: cmd + shift + left expands left to beginning of line', (tester) async { + testWidgets( + 'Mac: cmd + shift + left expands left to beginning of line', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -952,20 +1011,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Windows + Linux: control + shift + left expands left to beginning of line', (tester) async { + testWidgets( + 'Windows + Linux: control + shift + left expands left to beginning of line', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 10), + selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -984,21 +1047,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it collapses downstream selection on left side', (tester) async { + testWidgets('it collapses downstream selection on left side', + (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 6, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -1012,21 +1078,24 @@ void main() { expect(controller.selection.extentOffset, 6); }); - testWidgets('it collapses upstream selection on left side', (tester) async { + testWidgets('it collapses upstream selection on left side', + (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 10, extentOffset: 6, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowLeft, physicalKey: PhysicalKeyboardKey.arrowLeft, @@ -1045,16 +1114,18 @@ void main() { testWidgets('it does nothing at end of text blob', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 16), + selection: const TextSelection.collapsed(offset: 16), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); // Move by character - final characterResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final characterResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1068,10 +1139,11 @@ void main() { expect(controller.selection.extentOffset, 16); // Move by word - final wordResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final wordResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1086,10 +1158,11 @@ void main() { expect(controller.selection.extentOffset, 16); // Move to end of line - final lineResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final lineResult = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1107,15 +1180,17 @@ void main() { testWidgets('it moves right by character', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1134,15 +1209,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 17), + selection: const TextSelection.collapsed(offset: 17), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1158,21 +1234,27 @@ void main() { // We should have gone from line 1 to line 2. Make double sure by // checking that the bounding box for the character that's now selected // does not sit at the top of the text box. - expect(selectableTextState.getCharacterBox(TextPosition(offset: 18)).top, isNonZero); + expect( + selectableTextState + .getCharacterBox(const TextPosition(offset: 18)) + .top, + isNonZero); }); testWidgets('it expands right by character', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1191,15 +1273,17 @@ void main() { testWidgets('it moves right by word', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1217,15 +1301,17 @@ void main() { testWidgets('it expands right by word', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1242,20 +1328,23 @@ void main() { expect(controller.selection.extentOffset, 10); }); - testWidgets('Mac: cmd + right moves right to end of line', (tester) async { + testWidgets('Mac: cmd + right moves right to end of line', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1272,20 +1361,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Windows + Linux: control + right moves right to end of line', (tester) async { + testWidgets( + 'Windows + Linux: control + right moves right to end of line', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1302,20 +1395,23 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Mac: cmd + shift + right expands right to end of line', (tester) async { + testWidgets('Mac: cmd + shift + right expands right to end of line', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1334,20 +1430,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Windows + Linux: control + shift + right expands right to end of line', (tester) async { + testWidgets( + 'Windows + Linux: control + shift + right expands right to end of line', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection.collapsed(offset: 6), + selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1366,21 +1466,24 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it collapses downstream selection on right side', (tester) async { + testWidgets('it collapses downstream selection on right side', + (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 6, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1394,21 +1497,24 @@ void main() { expect(controller.selection.extentOffset, 10); }); - testWidgets('it collapses upstream selection on right side', (tester) async { + testWidgets('it collapses upstream selection on right side', + (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 10, extentOffset: 6, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowRight, physicalKey: PhysicalKeyboardKey.arrowRight, @@ -1424,20 +1530,22 @@ void main() { }); group('up arrow', () { - testWidgets('it moves to start of text when in first line', (tester) async { + testWidgets('it moves to start of text when in first line', + (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 5), + selection: const TextSelection.collapsed(offset: 5), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowUp, physicalKey: PhysicalKeyboardKey.arrowUp, @@ -1456,15 +1564,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 18), + selection: const TextSelection.collapsed(offset: 18), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowUp, physicalKey: PhysicalKeyboardKey.arrowUp, @@ -1483,15 +1592,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 18), + selection: const TextSelection.collapsed(offset: 18), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowUp, physicalKey: PhysicalKeyboardKey.arrowUp, @@ -1507,20 +1617,22 @@ void main() { expect(controller.selection.baseOffset, 18); }); - testWidgets('it preserves horizontal position in previous line', (tester) async { + testWidgets('it preserves horizontal position in previous line', + (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 23), + selection: const TextSelection.collapsed(offset: 23), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowUp, physicalKey: PhysicalKeyboardKey.arrowUp, @@ -1536,20 +1648,22 @@ void main() { }); group('down arrow', () { - testWidgets('it moves to end of text when in last line', (tester) async { + testWidgets('it moves to end of text when in last line', + (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 50), + selection: const TextSelection.collapsed(offset: 50), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowDown, physicalKey: PhysicalKeyboardKey.arrowDown, @@ -1560,7 +1674,8 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); - expect(controller.selection.extentOffset, _multilineLayoutText.length); + expect( + controller.selection.extentOffset, _multilineLayoutText.length); }); testWidgets('it moves to next line', (tester) async { @@ -1568,15 +1683,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowDown, physicalKey: PhysicalKeyboardKey.arrowDown, @@ -1595,15 +1711,16 @@ void main() { // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowDown, physicalKey: PhysicalKeyboardKey.arrowDown, @@ -1619,20 +1736,22 @@ void main() { expect(controller.selection.baseOffset, 0); }); - testWidgets('it preserves horizontal position in next line', (tester) async { + testWidgets('it preserves horizontal position in next line', + (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 5), + selection: const TextSelection.collapsed(offset: 5), ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers + .moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.arrowDown, physicalKey: PhysicalKeyboardKey.arrowDown, @@ -1650,23 +1769,26 @@ void main() { group('delete line before caret', () { group('Mac', () { - testWidgets('cmd + backspace deletes partial line before caret (flowed multiline)', (tester) async { + testWidgets( + 'cmd + backspace deletes partial line before caret (flowed multiline)', + (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 28), // midway through 2nd line + selection: const TextSelection.collapsed( + offset: 28), // midway through 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1679,28 +1801,32 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, 'this text is long be multiline in the available space'); + expect(controller.text.text, + 'this text is long be multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets('cmd + backspace deletes entire line (flowed multiline)', (tester) async { + testWidgets('cmd + backspace deletes entire line (flowed multiline)', + (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 31, affinity: TextAffinity.upstream), // end of 2nd line + selection: const TextSelection.collapsed( + offset: 31, + affinity: TextAffinity.upstream), // end of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1713,26 +1839,32 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, 'this text is long multiline in the available space'); + expect(controller.text.text, + 'this text is long multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets('cmd + backspace deletes partial line before caret (explicit newlines)', (tester) async { + testWidgets( + 'cmd + backspace deletes partial line before caret (explicit newlines)', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: TextSelection.collapsed(offset: 23), // midway through 2nd line + text: AttributedText( + text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed( + offset: 23), // midway through 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1745,26 +1877,32 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 15); - expect(controller.text.text, 'This is line 1\nline 2\nThis is line 3'); + expect( + controller.text.text, 'This is line 1\nline 2\nThis is line 3'); Platform.setTestInstance(null); }); - testWidgets('cmd + backspace deletes entire line (explicit newlines)', (tester) async { + testWidgets('cmd + backspace deletes entire line (explicit newlines)', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: TextSelection.collapsed(offset: 29, affinity: TextAffinity.upstream), // end of 2nd line + text: AttributedText( + text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed( + offset: 29, + affinity: TextAffinity.upstream), // end of 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1782,24 +1920,28 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is expanded', (tester) async { + testWidgets('it does nothing when selection is expanded', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), - selection: TextSelection( + text: AttributedText( + text: + 'This is some text that doesn\'t matter for this test.'), + selection: const TextSelection( baseOffset: 0, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1814,21 +1956,25 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection extent is < 0', (tester) async { + testWidgets('it does nothing when selection extent is < 0', + (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), - selection: TextSelection.collapsed(offset: -1), + text: AttributedText( + text: + 'This is some text that doesn\'t matter for this test.'), + selection: const TextSelection.collapsed(offset: -1), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1843,23 +1989,25 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is at start of line', (tester) async { + testWidgets('it does nothing when selection is at start of line', + (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 18), // start of 2nd line + selection: const TextSelection.collapsed( + offset: 18), // start of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1876,23 +2024,26 @@ void main() { }); group('Windows + Linux', () { - testWidgets('control + backspace deletes partial line before caret (flowed multiline)', (tester) async { + testWidgets( + 'control + backspace deletes partial line before caret (flowed multiline)', + (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 28), // midway through 2nd line + selection: const TextSelection.collapsed( + offset: 28), // midway through 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1905,28 +2056,33 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, 'this text is long be multiline in the available space'); + expect(controller.text.text, + 'this text is long be multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets('control + backspace deletes entire line (flowed multiline)', (tester) async { + testWidgets( + 'control + backspace deletes entire line (flowed multiline)', + (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 31, affinity: TextAffinity.upstream), // end of 2nd line + selection: const TextSelection.collapsed( + offset: 31, + affinity: TextAffinity.upstream), // end of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1939,26 +2095,32 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, 'this text is long multiline in the available space'); + expect(controller.text.text, + 'this text is long multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets('control + backspace deletes partial line before caret (explicit newlines)', (tester) async { + testWidgets( + 'control + backspace deletes partial line before caret (explicit newlines)', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: TextSelection.collapsed(offset: 23), // midway through 2nd line + text: AttributedText( + text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed( + offset: 23), // midway through 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -1971,26 +2133,33 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 15); - expect(controller.text.text, 'This is line 1\nline 2\nThis is line 3'); + expect( + controller.text.text, 'This is line 1\nline 2\nThis is line 3'); Platform.setTestInstance(null); }); - testWidgets('control + backspace deletes entire line (explicit newlines)', (tester) async { + testWidgets( + 'control + backspace deletes entire line (explicit newlines)', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: TextSelection.collapsed(offset: 29, affinity: TextAffinity.upstream), // end of 2nd line + text: AttributedText( + text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed( + offset: 29, + affinity: TextAffinity.upstream), // end of 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2008,24 +2177,28 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is expanded', (tester) async { + testWidgets('it does nothing when selection is expanded', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), - selection: TextSelection( + text: AttributedText( + text: + 'This is some text that doesn\'t matter for this test.'), + selection: const TextSelection( baseOffset: 0, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2040,21 +2213,25 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection extent is < 0', (tester) async { + testWidgets('it does nothing when selection extent is < 0', + (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), - selection: TextSelection.collapsed(offset: -1), + text: AttributedText( + text: + 'This is some text that doesn\'t matter for this test.'), + selection: const TextSelection.collapsed(offset: -1), ); - final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText( + tester, controller.text.text); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2069,23 +2246,25 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is at start of line', (tester) async { + testWidgets('it does nothing when selection is at start of line', + (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: TextSelection.collapsed(offset: 18), // start of 2nd line + selection: const TextSelection.collapsed( + offset: 18), // start of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = - DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2106,12 +2285,13 @@ void main() { test('it does nothing when text is empty', () { final controller = AttributedTextEditingController( text: AttributedText(text: ''), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2129,12 +2309,13 @@ void main() { test('it does nothing at beginning of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2152,12 +2333,13 @@ void main() { test('it deletes the previous character', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2175,15 +2357,16 @@ void main() { test('it deletes selected text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 8, extentOffset: 13, ), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.backspace, physicalKey: PhysicalKeyboardKey.backspace, @@ -2203,12 +2386,13 @@ void main() { test('it does nothing when text is empty', () { final controller = AttributedTextEditingController( text: AttributedText(text: ''), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.delete, physicalKey: PhysicalKeyboardKey.delete, @@ -2226,12 +2410,13 @@ void main() { test('it does nothing at beginning of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 17), + selection: const TextSelection.collapsed(offset: 17), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.delete, physicalKey: PhysicalKeyboardKey.delete, @@ -2249,12 +2434,13 @@ void main() { test('it deletes the next character', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 2), + selection: const TextSelection.collapsed(offset: 2), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.delete, physicalKey: PhysicalKeyboardKey.delete, @@ -2272,15 +2458,16 @@ void main() { test('it deletes selected text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection( + selection: const TextSelection( baseOffset: 8, extentOffset: 13, ), ); - final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.delete, physicalKey: PhysicalKeyboardKey.delete, @@ -2300,12 +2487,13 @@ void main() { test('inserts newline in middle of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 8), + selection: const TextSelection.collapsed(offset: 8), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertNewlineWhenEnterIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.enter, physicalKey: PhysicalKeyboardKey.enter, @@ -2323,12 +2511,13 @@ void main() { test('inserts newline at beginning of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertNewlineWhenEnterIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.enter, physicalKey: PhysicalKeyboardKey.enter, @@ -2346,12 +2535,13 @@ void main() { test('inserts newline at end of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: 'this is some text'), - selection: TextSelection.collapsed(offset: 17), + selection: const TextSelection.collapsed(offset: 17), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertNewlineWhenEnterIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.enter, physicalKey: PhysicalKeyboardKey.enter, @@ -2371,12 +2561,13 @@ void main() { test('inserts character in empty text', () { final controller = AttributedTextEditingController( text: AttributedText(text: ''), - selection: TextSelection.collapsed(offset: 0), + selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertCharacterWhenKeyIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyF, physicalKey: PhysicalKeyboardKey.keyF, @@ -2394,12 +2585,13 @@ void main() { test('inserts character in middle of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: '--><--'), - selection: TextSelection.collapsed(offset: 3), + selection: const TextSelection.collapsed(offset: 3), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertCharacterWhenKeyIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyF, physicalKey: PhysicalKeyboardKey.keyF, @@ -2417,12 +2609,13 @@ void main() { test('inserts character at end of text', () { final controller = AttributedTextEditingController( text: AttributedText(text: '-->'), - selection: TextSelection.collapsed(offset: 3), + selection: const TextSelection.collapsed(offset: 3), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertCharacterWhenKeyIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyF, physicalKey: PhysicalKeyboardKey.keyF, @@ -2440,12 +2633,13 @@ void main() { test('replaces selected text with character', () { final controller = AttributedTextEditingController( text: AttributedText(text: '-->REPLACE<--'), - selection: TextSelection(baseOffset: 3, extentOffset: 10), + selection: const TextSelection(baseOffset: 3, extentOffset: 10), ); - final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers + .insertCharacterWhenKeyIsPressed( controller: controller, - keyEvent: FakeRawKeyEvent( + keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.keyF, physicalKey: PhysicalKeyboardKey.keyF, @@ -2464,7 +2658,8 @@ void main() { }); } -final _multilineLayoutText = 'this text is long enough to be multiline in the available space'; +const _multilineLayoutText = + 'this text is long enough to be multiline in the available space'; // Based on experiments, the text is laid out as follows: // @@ -2506,10 +2701,11 @@ Future _pumpAndReturnSelectableText( final selectableText = SuperSelectableText.plain( key: textKey, text: text, - style: TextStyle(), + style: const TextStyle(), ); - final decoratedText = decorator == null ? selectableText : decorator(selectableText); + final decoratedText = + decorator == null ? selectableText : decorator(selectableText); await tester.pumpWidget( MaterialApp( From 53c348db1811d9294a00a8b04a2667b4ae495fa1 Mon Sep 17 00:00:00 2001 From: Brett Morgan Date: Fri, 4 Jun 2021 14:45:47 +1000 Subject: [PATCH 2/3] Reformat to 120 chars --- .../lib/demos/demo_attributed_text.dart | 21 +- .../demos/demo_markdown_serialization.dart | 6 +- .../lib/demos/demo_selectable_text.dart | 13 +- .../lib/demos/example_editor/_toolbar.dart | 88 +-- .../demos/supertextfield/_emojis_demo.dart | 13 +- .../_expanding_multi_line_demo.dart | 9 +- .../supertextfield/_interactive_demo.dart | 43 +- .../lib/demos/supertextfield/_robot.dart | 34 +- .../supertextfield/_single_line_demo.dart | 17 +- .../_static_multi_line_demo.dart | 9 +- super_editor/example/lib/main.dart | 7 +- .../marketing_video/main_marketing_video.dart | 13 +- .../attributed_spans_test.dart | 200 ++---- .../attributed_text_test.dart | 52 +- .../lib/src/core/document_editor.dart | 10 +- .../lib/src/default_editor/blockquote.dart | 41 +- .../lib/src/default_editor/box_component.dart | 44 +- .../common_editor_operations.dart | 328 ++++------ .../default_editor/document_interaction.dart | 119 ++-- .../document_keyboard_actions.dart | 105 ++-- .../src/default_editor/horizontal_rule.dart | 37 +- .../lib/src/default_editor/image.dart | 41 +- .../lib/src/default_editor/list_items.dart | 95 +-- .../lib/src/default_editor/super_editor.dart | 9 +- super_editor/lib/src/default_editor/text.dart | 243 +++----- .../lib/src/default_editor/text_tools.dart | 9 +- .../src/infrastructure/attributed_spans.dart | 197 ++---- .../src/infrastructure/multi_tap_gesture.dart | 2 +- .../infrastructure/super_selectable_text.dart | 80 +-- .../src/infrastructure/super_textfield.dart | 302 +++------ .../lib/src/infrastructure/text_layout.dart | 12 +- .../lib/src/serialization/markdown.dart | 23 +- .../editor_entry_smoke_test.dart | 6 +- .../test/serialization/markdown_test.dart | 168 ++--- .../src/default_editor/attributions_test.dart | 8 +- .../document_keyboard_actions_test.dart | 15 +- .../test/src/default_editor/text_test.dart | 3 +- .../infrastructure/attributed_spans_test.dart | 206 ++---- .../infrastructure/attributed_text_test.dart | 77 +-- .../infrastructure/super_textfield_test.dart | 588 ++++++------------ 40 files changed, 1012 insertions(+), 2281 deletions(-) diff --git a/super_editor/example/lib/demos/demo_attributed_text.dart b/super_editor/example/lib/demos/demo_attributed_text.dart index a0d15ae8af..a1b57b76cf 100644 --- a/super_editor/example/lib/demos/demo_attributed_text.dart +++ b/super_editor/example/lib/demos/demo_attributed_text.dart @@ -202,8 +202,7 @@ class _TextRangeSelectorState extends State { } void _onTapUp(TapUpDetails details) { - final selectedCellIndex = - _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); setState(() { _selectedCells[selectedCellIndex] = !_selectedCells[selectedCellIndex]; _reportSelectedRanges(); @@ -211,14 +210,12 @@ class _TextRangeSelectorState extends State { } void _onPanStart(DragStartDetails details) { - final selectedCellIndex = - _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); _selectionMode = _selectedCells[selectedCellIndex] ? 'deselect' : 'select'; } void _onPanUpdate(DragUpdateDetails details) { - final selectedCellIndex = - _getCellIndexFromLocalOffset(details.localPosition); + final selectedCellIndex = _getCellIndexFromLocalOffset(details.localPosition); setState(() { _selectedCells[selectedCellIndex] = _selectionMode == 'select'; _reportSelectedRanges(); @@ -226,9 +223,7 @@ class _TextRangeSelectorState extends State { } int _getCellIndexFromLocalOffset(Offset localOffset) { - return ((localOffset.dx / widget.cellWidth).floor()) - .clamp(0.0, widget.cellCount - 1) - .toInt(); + return ((localOffset.dx / widget.cellWidth).floor()).clamp(0.0, widget.cellCount - 1).toInt(); } void _reportSelectedRanges() { @@ -269,12 +264,8 @@ class _TextRangeSelectorState extends State { width: widget.cellWidth, height: widget.cellHeight, decoration: BoxDecoration( - border: Border.all( - width: 1, - color: _isSelected(index) ? Colors.red : Colors.grey), - color: _isSelected(index) - ? Colors.red.withOpacity(0.7) - : Colors.grey.withOpacity(0.7), + border: Border.all(width: 1, color: _isSelected(index) ? Colors.red : Colors.grey), + color: _isSelected(index) ? Colors.red.withOpacity(0.7) : Colors.grey.withOpacity(0.7), ), ), ), diff --git a/super_editor/example/lib/demos/demo_markdown_serialization.dart b/super_editor/example/lib/demos/demo_markdown_serialization.dart index e99ac045fd..a1e840622f 100644 --- a/super_editor/example/lib/demos/demo_markdown_serialization.dart +++ b/super_editor/example/lib/demos/demo_markdown_serialization.dart @@ -11,8 +11,7 @@ import 'package:super_editor/super_editor.dart'; /// current structure of the document in the editor. class MarkdownSerializationDemo extends StatefulWidget { @override - _MarkdownSerializationDemoState createState() => - _MarkdownSerializationDemoState(); + _MarkdownSerializationDemoState createState() => _MarkdownSerializationDemoState(); } class _MarkdownSerializationDemoState extends State { @@ -76,8 +75,7 @@ class _MarkdownSerializationDemoState extends State { color: const Color(0xFF222222), child: SingleChildScrollView( child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 24, vertical: 24), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), child: Text( _markdown, style: const TextStyle( diff --git a/super_editor/example/lib/demos/demo_selectable_text.dart b/super_editor/example/lib/demos/demo_selectable_text.dart index 4268b5ced4..3d38c977a1 100644 --- a/super_editor/example/lib/demos/demo_selectable_text.dart +++ b/super_editor/example/lib/demos/demo_selectable_text.dart @@ -69,8 +69,7 @@ class _SelectableTextDemoState extends State { title: 'TEXT WITH CARET + COLLAPSED SELECTION', demo: SuperSelectableText( textSpan: _demoText1, - textSelection: TextSelection.collapsed( - offset: _demoText1.toPlainText().length), + textSelection: TextSelection.collapsed(offset: _demoText1.toPlainText().length), showCaret: true, ), ), @@ -79,8 +78,7 @@ class _SelectableTextDemoState extends State { title: 'TEXT WITH LEFT-TO-RIGHT SELECTION + CARET', demo: SuperSelectableText( textSpan: _demoText1, - textSelection: - const TextSelection(baseOffset: 0, extentOffset: 12), + textSelection: const TextSelection(baseOffset: 0, extentOffset: 12), showCaret: true, ), ), @@ -97,8 +95,7 @@ class _SelectableTextDemoState extends State { ), const SizedBox(height: 24), _buildDemo( - title: - 'TEXT WITH FULL SELECTION + CARET, CUSTOM COLORS, CARET SHAPE, DEBUG PAINT', + title: 'TEXT WITH FULL SELECTION + CARET, CUSTOM COLORS, CARET SHAPE, DEBUG PAINT', demo: DebugSelectableTextDecorator( selectableTextKey: _debugTextKey, textLength: _demoText1.toPlainText().length, @@ -106,9 +103,7 @@ class _SelectableTextDemoState extends State { child: SuperSelectableText( key: _debugTextKey, textSpan: _demoText1, - textSelection: TextSelection( - baseOffset: 0, - extentOffset: _demoText1.toPlainText().length), + textSelection: TextSelection(baseOffset: 0, extentOffset: _demoText1.toPlainText().length), textSelectionDecoration: const TextSelectionDecoration( selectionColor: Colors.yellow, ), diff --git a/super_editor/example/lib/demos/example_editor/_toolbar.dart b/super_editor/example/lib/demos/example_editor/_toolbar.dart index b7d1c32159..63542e1d51 100644 --- a/super_editor/example/lib/demos/example_editor/_toolbar.dart +++ b/super_editor/example/lib/demos/example_editor/_toolbar.dart @@ -76,8 +76,7 @@ class _EditorToolbarState extends State { return false; } - final selectedNode = - widget.editor!.document.getNodeById(selection.extent.nodeId); + final selectedNode = widget.editor!.document.getNodeById(selection.extent.nodeId); return selectedNode is ParagraphNode || selectedNode is ListItemNode; } @@ -85,8 +84,7 @@ class _EditorToolbarState extends State { /// /// Throws an exception if the currently selected node is not a text node. _TextType _getCurrentTextType() { - final selectedNode = widget.editor!.document - .getNodeById(widget.composer!.selection!.extent.nodeId); + final selectedNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId); if (selectedNode is ParagraphNode) { final type = selectedNode.metadata['blockType']; @@ -102,9 +100,7 @@ class _EditorToolbarState extends State { return _TextType.paragraph; } } else if (selectedNode is ListItemNode) { - return selectedNode.type == ListItemType.ordered - ? _TextType.orderedListItem - : _TextType.unorderedListItem; + return selectedNode.type == ListItemType.ordered ? _TextType.orderedListItem : _TextType.unorderedListItem; } else { throw Exception('Invalid node type: $selectedNode'); } @@ -114,8 +110,7 @@ class _EditorToolbarState extends State { /// /// Throws an exception if the currently selected node is not a text node. TextAlign _getCurrentTextAlignment() { - final selectedNode = widget.editor!.document - .getNodeById(widget.composer!.selection!.extent.nodeId); + final selectedNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId); if (selectedNode is ParagraphNode) { final align = selectedNode.metadata['textAlign']; switch (align) { @@ -131,8 +126,7 @@ class _EditorToolbarState extends State { return TextAlign.left; } } else { - throw Exception( - 'Alignment does not apply to node of type: $selectedNode'); + throw Exception('Alignment does not apply to node of type: $selectedNode'); } } @@ -144,8 +138,7 @@ class _EditorToolbarState extends State { return false; } - final selectedNode = - widget.editor!.document.getNodeById(selection.extent.nodeId); + final selectedNode = widget.editor!.document.getNodeById(selection.extent.nodeId); return selectedNode is ParagraphNode; } @@ -166,9 +159,7 @@ class _EditorToolbarState extends State { widget.editor!.executeCommand( ChangeListItemTypeCommand( nodeId: widget.composer!.selection!.extent.nodeId, - newType: newType == _TextType.orderedListItem - ? ListItemType.ordered - : ListItemType.unordered, + newType: newType == _TextType.orderedListItem ? ListItemType.ordered : ListItemType.unordered, ), ); } else if (_isListItem(existingTextType) && !_isListItem(newType)) { @@ -184,25 +175,20 @@ class _EditorToolbarState extends State { widget.editor!.executeCommand( ConvertParagraphToListItemCommand( nodeId: widget.composer!.selection!.extent.nodeId, - type: newType == _TextType.orderedListItem - ? ListItemType.ordered - : ListItemType.unordered, + type: newType == _TextType.orderedListItem ? ListItemType.ordered : ListItemType.unordered, ), ); } else { // Apply a new block type to an existing paragraph node. - final existingNode = widget.editor!.document - .getNodeById(widget.composer!.selection!.extent.nodeId)!; - (existingNode as ParagraphNode).metadata['blockType'] = - _getBlockTypeAttribution(newType); + final existingNode = widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId)!; + (existingNode as ParagraphNode).metadata['blockType'] = _getBlockTypeAttribution(newType); } } /// Returns true if the given [_TextType] represents an /// ordered or unordered list item, returns false otherwise. bool _isListItem(_TextType? type) { - return type == _TextType.orderedListItem || - type == _TextType.unorderedListItem; + return type == _TextType.orderedListItem || type == _TextType.unorderedListItem; } /// Returns the text [Attribution] associated with the given @@ -274,16 +260,13 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = - TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document - .getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final overlappingLinkAttributions = text.getAttributionSpansInRange( - attributionFilter: (Attribution attribution) => - attribution is LinkAttribution, + attributionFilter: (Attribution attribution) => attribution is LinkAttribution, range: selectionRange, ); @@ -298,16 +281,13 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = - TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document - .getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final overlappingLinkAttributions = text.getAttributionSpansInRange( - attributionFilter: (Attribution attribution) => - attribution is LinkAttribution, + attributionFilter: (Attribution attribution) => attribution is LinkAttribution, range: selectionRange, ); @@ -320,10 +300,8 @@ class _EditorToolbarState extends State { // The selected text contains one other link. final overlappingLinkSpan = overlappingLinkAttributions.first; final isLinkSelectionOnTrailingEdge = - (overlappingLinkSpan.start >= selectionRange.start && - overlappingLinkSpan.start <= selectionRange.end) || - (overlappingLinkSpan.end >= selectionRange.start && - overlappingLinkSpan.end <= selectionRange.end); + (overlappingLinkSpan.start >= selectionRange.start && overlappingLinkSpan.start <= selectionRange.end) || + (overlappingLinkSpan.end >= selectionRange.start && overlappingLinkSpan.end <= selectionRange.end); if (isLinkSelectionOnTrailingEdge) { // The selected text covers the beginning, or the end, or the entire @@ -334,8 +312,7 @@ class _EditorToolbarState extends State { // the entire link attribution. text.removeAttribution( overlappingLinkSpan.attribution, - TextRange( - start: overlappingLinkSpan.start, end: overlappingLinkSpan.end), + TextRange(start: overlappingLinkSpan.start, end: overlappingLinkSpan.end), ); } } else { @@ -357,11 +334,9 @@ class _EditorToolbarState extends State { final extentOffset = (selection.extent.nodePosition as TextPosition).offset; final selectionStart = min(baseOffset, extentOffset); final selectionEnd = max(baseOffset, extentOffset); - final selectionRange = - TextRange(start: selectionStart, end: selectionEnd - 1); + final selectionRange = TextRange(start: selectionStart, end: selectionEnd - 1); - final textNode = widget.editor!.document - .getNodeById(selection.extent.nodeId) as TextNode; + final textNode = widget.editor!.document.getNodeById(selection.extent.nodeId) as TextNode; final text = textNode.text; final trimmedRange = _trimTextRangeWhitespace(text, selectionRange); @@ -376,8 +351,7 @@ class _EditorToolbarState extends State { _urlController!.clear(); setState(() { _showUrlField = false; - _urlFocusNode! - .unfocus(disposition: UnfocusDisposition.previouslyFocusedChild); + _urlFocusNode!.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild); widget.closeToolbar(); }); } @@ -423,9 +397,8 @@ class _EditorToolbarState extends State { break; } - final selectedNode = widget.editor!.document - .getNodeById(widget.composer!.selection!.extent.nodeId) - as ParagraphNode; + final selectedNode = + widget.editor!.document.getNodeById(widget.composer!.selection!.extent.nodeId) as ParagraphNode; selectedNode.metadata['textAlign'] = newAlignmentValue; } @@ -557,9 +530,7 @@ class _EditorToolbarState extends State { child: IconButton( onPressed: _areMultipleLinksSelected() ? null : _onLinkPressed, icon: const Icon(Icons.link), - color: _isSingleLinkSelected() - ? const Color(0xFF007AFF) - : IconTheme.of(context).color, + color: _isSingleLinkSelected() ? const Color(0xFF007AFF) : IconTheme.of(context).color, splashRadius: 16, tooltip: AppLocalizations.of(context)!.labelLink, ), @@ -572,12 +543,7 @@ class _EditorToolbarState extends State { message: AppLocalizations.of(context)!.labelTextAlignment, child: DropdownButton( value: _getCurrentTextAlignment(), - items: [ - TextAlign.left, - TextAlign.center, - TextAlign.right, - TextAlign.justify - ] + items: [TextAlign.left, TextAlign.center, TextAlign.right, TextAlign.justify] .map((textAlign) => DropdownMenuItem( value: textAlign, child: Padding( diff --git a/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart b/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart index d3651292ae..9d0f3dfb9b 100644 --- a/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_emojis_demo.dart @@ -15,8 +15,7 @@ class EmojisTextFieldDemo extends StatefulWidget { _EmojisTextFieldDemoState createState() => _EmojisTextFieldDemoState(); } -class _EmojisTextFieldDemoState extends State - with TickerProviderStateMixin { +class _EmojisTextFieldDemoState extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController(); GlobalKey? _textKey; @@ -57,8 +56,7 @@ class _EmojisTextFieldDemoState extends State if (widget.direction == TextAffinity.upstream) { // simulate pressing backspace _demoRobot - ..insertCaretAt( - TextPosition(offset: _textFieldController.text.text.length)) + ..insertCaretAt(TextPosition(offset: _textFieldController.text.text.length)) ..pause(const Duration(seconds: 1)) ..backspaceCharacters(_textFieldController.text.text.length) ..start(); @@ -101,16 +99,13 @@ class _EmojisTextFieldDemoState extends State key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus - ? Colors.blue - : Colors.grey.shade300, + color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, width: 1, ), ), diff --git a/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart index 3d6cf84319..45f850449e 100644 --- a/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_expanding_multi_line_demo.dart @@ -5,12 +5,10 @@ import '_robot.dart'; class ExpandingMultiLineTextFieldDemo extends StatefulWidget { @override - _ExpandingMultiLineTextFieldDemoState createState() => - _ExpandingMultiLineTextFieldDemoState(); + _ExpandingMultiLineTextFieldDemoState createState() => _ExpandingMultiLineTextFieldDemoState(); } -class _ExpandingMultiLineTextFieldDemoState - extends State +class _ExpandingMultiLineTextFieldDemoState extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( @@ -95,8 +93,7 @@ class _ExpandingMultiLineTextFieldDemoState key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Material( borderRadius: BorderRadius.circular(4), diff --git a/super_editor/example/lib/demos/supertextfield/_interactive_demo.dart b/super_editor/example/lib/demos/supertextfield/_interactive_demo.dart index 441b267dbf..96ca06f215 100644 --- a/super_editor/example/lib/demos/supertextfield/_interactive_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_interactive_demo.dart @@ -7,32 +7,18 @@ const flutterAttribution = NamedAttribution('flutter'); class InteractiveTextFieldDemo extends StatefulWidget { @override - _InteractiveTextFieldDemoState createState() => - _InteractiveTextFieldDemoState(); + _InteractiveTextFieldDemoState createState() => _InteractiveTextFieldDemoState(); } class _InteractiveTextFieldDemoState extends State { final _textFieldController = AttributedTextEditingController( text: AttributedText( - text: - 'Super Editor is an open source text editor for Flutter projects.', + text: 'Super Editor is an open source text editor for Flutter projects.', spans: AttributedSpans(attributions: const [ - SpanMarker( - attribution: brandAttribution, - offset: 0, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: brandAttribution, - offset: 11, - markerType: SpanMarkerType.end), - SpanMarker( - attribution: flutterAttribution, - offset: 47, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: flutterAttribution, - offset: 53, - markerType: SpanMarkerType.end), + SpanMarker(attribution: brandAttribution, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: brandAttribution, offset: 11, markerType: SpanMarkerType.end), + SpanMarker(attribution: flutterAttribution, offset: 47, markerType: SpanMarkerType.start), + SpanMarker(attribution: flutterAttribution, offset: 53, markerType: SpanMarkerType.end), ])), ); @@ -53,8 +39,8 @@ class _InteractiveTextFieldDemoState extends State { super.dispose(); } - void _onRightClick(BuildContext textFieldContext, - AttributedTextEditingController textController, Offset localOffset) { + void _onRightClick( + BuildContext textFieldContext, AttributedTextEditingController textController, Offset localOffset) { // Only show menu if some text is selected if (textController.selection.isCollapsed) { return; @@ -63,8 +49,7 @@ class _InteractiveTextFieldDemoState extends State { final overlay = Overlay.of(context)!; final overlayBox = overlay.context.findRenderObject() as RenderBox?; final textFieldBox = textFieldContext.findRenderObject() as RenderBox; - _popupOffset = - textFieldBox.localToGlobal(localOffset, ancestor: overlayBox); + _popupOffset = textFieldBox.localToGlobal(localOffset, ancestor: overlayBox); if (_popupEntry == null) { _popupEntry = OverlayEntry(builder: (context) { @@ -99,8 +84,7 @@ class _InteractiveTextFieldDemoState extends State { TextButton( onPressed: () { Clipboard.setData(ClipboardData( - text: textController.selection - .textInside(textController.text.text), + text: textController.selection.textInside(textController.text.text), )); _closePopup(); }, @@ -152,16 +136,13 @@ class _InteractiveTextFieldDemoState extends State { textController: _textFieldController, focusNode: _focusNode, textStyleBuilder: _textStyleBuilder, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus - ? Colors.blue - : Colors.grey.shade300, + color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, width: 1, ), ), diff --git a/super_editor/example/lib/demos/supertextfield/_robot.dart b/super_editor/example/lib/demos/supertextfield/_robot.dart index 9be8867cda..a66ce13d6e 100644 --- a/super_editor/example/lib/demos/supertextfield/_robot.dart +++ b/super_editor/example/lib/demos/supertextfield/_robot.dart @@ -59,8 +59,7 @@ class TextFieldDemoRobot { } Future deselect() async { - _commands.add(SelectTextCommand( - selection: const TextSelection.collapsed(offset: -1))); + _commands.add(SelectTextCommand(selection: const TextSelection.collapsed(offset: -1))); } Future pause(Duration duration) async { @@ -133,8 +132,7 @@ class TypeTextCommand implements RobotCommand { GlobalKey? textKey, ) async { if (textController.selection.extentOffset == -1) { - print( - 'Can\'t type text because the text field doesn\'t have a valid selection.'); + print('Can\'t type text because the text field doesn\'t have a valid selection.'); return; } @@ -151,19 +149,15 @@ class TypeTextCommand implements RobotCommand { } } - void _typeCharacter( - AttributedTextEditingController textController, int offset) { + void _typeCharacter(AttributedTextEditingController textController, int offset) { textController.text = textController.text.insertString( - textToInsert: - textToType.text[offset], // TODO: support insertion of attributed text + textToInsert: textToType.text[offset], // TODO: support insertion of attributed text startOffset: textController.selection.extentOffset, ); final previousSelection = textController.selection; textController.selection = TextSelection( - baseOffset: previousSelection.isCollapsed - ? previousSelection.extentOffset + 1 - : previousSelection.baseOffset, + baseOffset: previousSelection.isCollapsed ? previousSelection.extentOffset + 1 : previousSelection.baseOffset, extentOffset: previousSelection.extentOffset + 1, ); } @@ -221,8 +215,7 @@ class DeleteCharactersCommand implements RobotCommand { GlobalKey? textKey, ) async { if (textController.selection.extentOffset == -1) { - print( - 'Can\'t delete characters because the text field doesn\'t have a valid selection.'); + print('Can\'t delete characters because the text field doesn\'t have a valid selection.'); return; } @@ -231,8 +224,7 @@ class DeleteCharactersCommand implements RobotCommand { int currentOffset = textController.selection.extentOffset; final finalLength = textController.text.text.length - characterCount; while (textController.text.text.length > finalLength) { - final codePointsDeleted = - _deleteCharacter(textController, currentOffset, direction); + final codePointsDeleted = _deleteCharacter(textController, currentOffset, direction); if (direction == TextAffinity.upstream) { currentOffset -= codePointsDeleted; } @@ -245,8 +237,7 @@ class DeleteCharactersCommand implements RobotCommand { } } - int _deleteCharacter(AttributedTextEditingController textController, - int offset, TextAffinity direction) { + int _deleteCharacter(AttributedTextEditingController textController, int offset, TextAffinity direction) { int deleteStartIndex; int deleteEndIndex; int deletedCodePointCount; @@ -260,8 +251,7 @@ class DeleteCharactersCommand implements RobotCommand { newSelectionIndex = deleteStartIndex; } else { // Delete the character before the offset - deleteStartIndex = - getCharacterStartBounds(textController.text.text, offset); + deleteStartIndex = getCharacterStartBounds(textController.text.text, offset); deleteEndIndex = offset + 1; deletedCodePointCount = offset - deleteStartIndex; newSelectionIndex = deleteStartIndex; @@ -272,8 +262,7 @@ class DeleteCharactersCommand implements RobotCommand { endOffset: deleteEndIndex, ); - textController.selection = - TextSelection.collapsed(offset: newSelectionIndex); + textController.selection = TextSelection.collapsed(offset: newSelectionIndex); return deletedCodePointCount; } @@ -323,8 +312,7 @@ class InsertCaretCommand implements RobotCommand { GlobalKey? textKey, ) async { focusNode!.requestFocus(); - textController.selection = - TextSelection.collapsed(offset: caretPosition.offset); + textController.selection = TextSelection.collapsed(offset: caretPosition.offset); } @override diff --git a/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart index cd9b5869db..f0c2531bec 100644 --- a/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_single_line_demo.dart @@ -5,12 +5,10 @@ import '_robot.dart'; class SingleLineTextFieldDemo extends StatefulWidget { @override - _SingleLineTextFieldDemoState createState() => - _SingleLineTextFieldDemoState(); + _SingleLineTextFieldDemoState createState() => _SingleLineTextFieldDemoState(); } -class _SingleLineTextFieldDemoState extends State - with TickerProviderStateMixin { +class _SingleLineTextFieldDemoState extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( // text: @@ -57,9 +55,7 @@ class _SingleLineTextFieldDemoState extends State ..selection = const TextSelection.collapsed(offset: 0) ..text = AttributedText(); _demoRobot - ..typeText(AttributedText( - text: - 'Hello World! This is a robot typing some text into a SuperTextField.')) + ..typeText(AttributedText(text: 'Hello World! This is a robot typing some text into a SuperTextField.')) ..start(); } @@ -92,16 +88,13 @@ class _SingleLineTextFieldDemoState extends State key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( - color: _focusNode!.hasFocus - ? Colors.blue - : Colors.grey.shade300, + color: _focusNode!.hasFocus ? Colors.blue : Colors.grey.shade300, width: 1, ), ), diff --git a/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart b/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart index 6e9b686fd4..e615ad7daa 100644 --- a/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart +++ b/super_editor/example/lib/demos/supertextfield/_static_multi_line_demo.dart @@ -5,12 +5,10 @@ import '_robot.dart'; class StaticMultiLineTextFieldDemo extends StatefulWidget { @override - _StaticMultiLineTextFieldDemoState createState() => - _StaticMultiLineTextFieldDemoState(); + _StaticMultiLineTextFieldDemoState createState() => _StaticMultiLineTextFieldDemoState(); } -class _StaticMultiLineTextFieldDemoState - extends State with TickerProviderStateMixin { +class _StaticMultiLineTextFieldDemoState extends State with TickerProviderStateMixin { final _textFieldController = AttributedTextEditingController( text: AttributedText( // text: @@ -94,8 +92,7 @@ class _StaticMultiLineTextFieldDemoState key: _textKey, textController: _textFieldController, focusNode: _focusNode, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decorationBuilder: (context, child) { return Material( borderRadius: BorderRadius.circular(4), diff --git a/super_editor/example/lib/main.dart b/super_editor/example/lib/main.dart index 6c79dcadad..6725214e6d 100644 --- a/super_editor/example/lib/main.dart +++ b/super_editor/example/lib/main.dart @@ -295,11 +295,10 @@ class _DrawerButton extends StatelessWidget { return Colors.transparent; }), // splashFactory: NoSplash.splashFactory, - foregroundColor: MaterialStateColor.resolveWith((states) => - isSelected ? Colors.white : const Color(0xFFBBBBBB)), + foregroundColor: + MaterialStateColor.resolveWith((states) => isSelected ? Colors.white : const Color(0xFFBBBBBB)), elevation: MaterialStateProperty.resolveWith((states) => 0), - padding: MaterialStateProperty.resolveWith( - (states) => const EdgeInsets.all(16))), + padding: MaterialStateProperty.resolveWith((states) => const EdgeInsets.all(16))), onPressed: isSelected ? null : onPressed, child: Row( children: [ diff --git a/super_editor/example/lib/marketing_video/main_marketing_video.dart b/super_editor/example/lib/marketing_video/main_marketing_video.dart index aeab7ff28d..eaef16b92a 100644 --- a/super_editor/example/lib/marketing_video/main_marketing_video.dart +++ b/super_editor/example/lib/marketing_video/main_marketing_video.dart @@ -250,8 +250,7 @@ class DocumentEditingRobot { _editorOps = CommonEditorOperations( editor: editor, composer: composer, - documentLayoutResolver: - documentLayoutFinder as DocumentLayout Function()), + documentLayoutResolver: documentLayoutFinder as DocumentLayout Function()), _random = Random(randomSeed); final DocumentEditor _editor; @@ -357,8 +356,7 @@ class DocumentEditingRobot { _editorOps.insertCharacter(character); if (character == ' ') { - _editorOps.convertParagraphByPatternMatching( - _composer.selection!.extent.nodeId); + _editorOps.convertParagraphByPatternMatching(_composer.selection!.extent.nodeId); } }, ), @@ -374,8 +372,7 @@ class DocumentEditingRobot { _editorOps.insertCharacter(character); if (character == ' ') { - _editorOps.convertParagraphByPatternMatching( - _composer.selection!.extent.nodeId); + _editorOps.convertParagraphByPatternMatching(_composer.selection!.extent.nodeId); } }, true, @@ -452,9 +449,7 @@ class DocumentEditingRobot { } Duration _randomWaitPeriod([bool fastMode = false]) { - return Duration( - milliseconds: - _random.nextInt(fastMode ? 45 : 200) + (fastMode ? 5 : 50)); + return Duration(milliseconds: _random.nextInt(fastMode ? 45 : 200) + (fastMode ? 5 : 50)); } Future start() async { diff --git a/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart b/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart index 2ec5599eae..89608789fa 100644 --- a/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart +++ b/super_editor/example/test/spikes/editor_abstractions/attributed_spans_test.dart @@ -11,17 +11,9 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 0, - markerType: SpanMarkerType.start)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 16, - markerType: SpanMarkerType.end)); + spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); }); test('applies attribution to beginning of span', () { @@ -31,17 +23,8 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 0, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 7, - markerType: SpanMarkerType.end)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); + expect(spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); }); test('applies attribution to inner span', () { @@ -51,17 +34,8 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 7, - markerType: SpanMarkerType.end)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); + expect(spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); }); test('applies attribution to end of span', () { @@ -71,17 +45,9 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 7, - markerType: SpanMarkerType.start)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.start)); expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 16, - markerType: SpanMarkerType.end)); + spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); }); test('applies exotic span', () { @@ -94,18 +60,10 @@ void main() { spans.addAttribution(newAttribution: linkAttribution, start: 2, end: 7); expect(spans.attributions.length, 2); + expect(spans.attributions[0], + SpanMarker(attribution: linkAttribution, offset: 2, markerType: SpanMarkerType.start)); expect( - spans.attributions[0], - SpanMarker( - attribution: linkAttribution, - offset: 2, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[1], - SpanMarker( - attribution: linkAttribution, - offset: 7, - markerType: SpanMarkerType.end)); + spans.attributions[1], SpanMarker(attribution: linkAttribution, offset: 7, markerType: SpanMarkerType.end)); expect(spans.getAllAttributionsAt(4).first, equals(linkAttribution)); }); @@ -113,12 +71,8 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 0, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); @@ -131,12 +85,8 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); @@ -149,12 +99,8 @@ void main() { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); @@ -162,29 +108,16 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 5, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 7, - markerType: SpanMarkerType.end)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 5, markerType: SpanMarkerType.start)); + expect(spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); }); test('removes attribution from partial inner span', () { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); @@ -192,41 +125,19 @@ void main() { expect(spans.attributions.length, 4); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); + expect(spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 3, markerType: SpanMarkerType.end)); expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 3, - markerType: SpanMarkerType.end)); - expect( - spans.attributions[2], - const SpanMarker( - attribution: 'bold', - offset: 6, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[3], - const SpanMarker( - attribution: 'bold', - offset: 7, - markerType: SpanMarkerType.end)); + spans.attributions[2], const SpanMarker(attribution: 'bold', offset: 6, markerType: SpanMarkerType.start)); + expect(spans.attributions[3], const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end)); }); test('removes attribution from partial ending span', () { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end) ], ); @@ -234,29 +145,16 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 2, - markerType: SpanMarkerType.start)); - expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 4, - markerType: SpanMarkerType.end)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start)); + expect(spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 4, markerType: SpanMarkerType.end)); }); test('applies attribution when mixed span is toggled', () { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 8, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 8, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); @@ -264,29 +162,17 @@ void main() { expect(spans.attributions.length, 2); expect( - spans.attributions[0], - const SpanMarker( - attribution: 'bold', - offset: 0, - markerType: SpanMarkerType.start)); + spans.attributions[0], const SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start)); expect( - spans.attributions[1], - const SpanMarker( - attribution: 'bold', - offset: 16, - markerType: SpanMarkerType.end)); + spans.attributions[1], const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end)); }); test('removes attribution when contiguous span is toggled', () { final spans = AttributedSpans( length: 17, attributions: [ - const SpanMarker( - attribution: 'bold', - offset: 0, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) + const SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: 'bold', offset: 16, markerType: SpanMarkerType.end) ], ); @@ -404,26 +290,18 @@ class _ExpectedSpans { late List _combinedSpans; void expectSpans(AttributedSpans spans) { - for (int characterIndex = 0; - characterIndex < _combinedSpans.length; - ++characterIndex) { - for (int attributionIndex = 0; - attributionIndex < _combinedSpans[characterIndex].length; - ++attributionIndex) { + for (int characterIndex = 0; characterIndex < _combinedSpans.length; ++characterIndex) { + for (int attributionIndex = 0; attributionIndex < _combinedSpans[characterIndex].length; ++attributionIndex) { print('Checking character $characterIndex'); // The attribution name is just a letter, like 'b', 'i', or 's'. - final attributionName = - _combinedSpans[characterIndex][attributionIndex]; + final attributionName = _combinedSpans[characterIndex][attributionIndex]; print(' - looking for attribution: "$attributionName"'); if (attributionName == '_') { print(' - skipping empty template character'); continue; } - expect( - spans.hasAttributionAt(characterIndex, - attribution: attributionName), - true); + expect(spans.hasAttributionAt(characterIndex, attribution: attributionName), true); } } } diff --git a/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart b/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart index 4b010e6564..4ec966afa2 100644 --- a/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart +++ b/super_editor/example/test/spikes/editor_abstractions/attributed_text_test.dart @@ -17,10 +17,8 @@ void main() { test('full-span style', () { final text = AttributedText(text: 'abcdefghij', attributions: const [ - SpanMarker( - attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'bold', offset: 9, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 9, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -31,10 +29,8 @@ void main() { test('single character style', () { final text = AttributedText(text: 'abcdefghij', attributions: const [ - SpanMarker( - attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -52,10 +48,8 @@ void main() { // Notice that the markers are provided in reverse order: // end then start. Order shouldn't matter within a single // position index. This test ensures that. - SpanMarker( - attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), - SpanMarker( - attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 1, markerType: SpanMarkerType.start), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -84,10 +78,8 @@ void main() { test('partial style', () { final text = AttributedText(text: 'abcdefghij', attributions: const [ - SpanMarker( - attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'bold', offset: 7, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 7, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -101,16 +93,10 @@ void main() { test('non-mingled varying styles', () { final text = AttributedText(text: 'abcdefghij', attributions: const [ - SpanMarker( - attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'bold', offset: 4, markerType: SpanMarkerType.end), - SpanMarker( - attribution: 'italics', - offset: 5, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'italics', offset: 9, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 4, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'italics', offset: 5, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'italics', offset: 9, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); @@ -126,16 +112,10 @@ void main() { test('intermingled varying styles', () { final text = AttributedText(text: 'abcdefghij', attributions: const [ - SpanMarker( - attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'italics', - offset: 4, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: 'bold', offset: 5, markerType: SpanMarkerType.end), - SpanMarker( - attribution: 'italics', offset: 7, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'bold', offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'italics', offset: 4, markerType: SpanMarkerType.start), + SpanMarker(attribution: 'bold', offset: 5, markerType: SpanMarkerType.end), + SpanMarker(attribution: 'italics', offset: 7, markerType: SpanMarkerType.end), ]); final textSpan = text.computeTextSpan(_styleBuilder); diff --git a/super_editor/lib/src/core/document_editor.dart b/super_editor/lib/src/core/document_editor.dart index 379e9b889d..2ed929816b 100644 --- a/super_editor/lib/src/core/document_editor.dart +++ b/super_editor/lib/src/core/document_editor.dart @@ -139,9 +139,7 @@ class MutableDocument with ChangeNotifier implements Document { @override DocumentNode? getNodeAfter(DocumentNode node) { final nodeIndex = getNodeIndex(node); - return nodeIndex >= 0 && nodeIndex < nodes.length - 1 - ? getNodeAt(nodeIndex + 1) - : null; + return nodeIndex >= 0 && nodeIndex < nodes.length - 1 ? getNodeAt(nodeIndex + 1) : null; } @override @@ -149,8 +147,7 @@ class MutableDocument with ChangeNotifier implements Document { _nodes.firstWhereOrNull((element) => element.id == position.nodeId); @override - DocumentRange getRangeBetween( - DocumentPosition position1, DocumentPosition position2) { + DocumentRange getRangeBetween(DocumentPosition position1, DocumentPosition position2) { final node1 = getNode(position1); if (node1 == null) { throw Exception('No such position in document: $position1'); @@ -170,8 +167,7 @@ class MutableDocument with ChangeNotifier implements Document { } @override - List getNodesInside( - DocumentPosition position1, DocumentPosition position2) { + List getNodesInside(DocumentPosition position1, DocumentPosition position2) { final node1 = getNode(position1); if (node1 == null) { throw Exception('No such position in document: $position1'); diff --git a/super_editor/lib/src/default_editor/blockquote.dart b/super_editor/lib/src/default_editor/blockquote.dart index eb61b372cd..64cefdda1a 100644 --- a/super_editor/lib/src/default_editor/blockquote.dart +++ b/super_editor/lib/src/default_editor/blockquote.dart @@ -102,10 +102,8 @@ ExecutionInstruction insertNewlineInBlockquote({ return ExecutionInstruction.continueExecution; } - final baseNode = editContext.editor.document - .getNodeById(editContext.composer.selection!.base.nodeId)!; - final extentNode = editContext.editor.document - .getNodeById(editContext.composer.selection!.extent.nodeId)!; + final baseNode = editContext.editor.document.getNodeById(editContext.composer.selection!.base.nodeId)!; + final extentNode = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return ExecutionInstruction.continueExecution; } @@ -117,9 +115,7 @@ ExecutionInstruction insertNewlineInBlockquote({ } final didInsertNewline = editContext.commonOps.insertPlainText('\n'); - return didInsertNewline - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didInsertNewline ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction splitBlockquoteWhenEnterPressed({ @@ -134,10 +130,8 @@ ExecutionInstruction splitBlockquoteWhenEnterPressed({ return ExecutionInstruction.continueExecution; } - final baseNode = editContext.editor.document - .getNodeById(editContext.composer.selection!.base.nodeId)!; - final extentNode = editContext.editor.document - .getNodeById(editContext.composer.selection!.extent.nodeId)!; + final baseNode = editContext.editor.document.getNodeById(editContext.composer.selection!.base.nodeId)!; + final extentNode = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return ExecutionInstruction.continueExecution; } @@ -149,9 +143,7 @@ ExecutionInstruction splitBlockquoteWhenEnterPressed({ } final didSplit = editContext.commonOps.insertBlockLevelNewline(); - return didSplit - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didSplit ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } class SplitBlockquoteCommand implements EditorCommand { @@ -171,9 +163,7 @@ class SplitBlockquoteCommand implements EditorCommand { final blockquote = node as ParagraphNode; final text = blockquote.text; final startText = text.copyText(0, splitPosition.offset); - final endText = splitPosition.offset < text.text.length - ? text.copyText(splitPosition.offset) - : AttributedText(); + final endText = splitPosition.offset < text.text.length ? text.copyText(splitPosition.offset) : AttributedText(); // Change the current node's content to just the text before the caret. // TODO: figure out how node changes should work in terms of @@ -186,8 +176,7 @@ class SplitBlockquoteCommand implements EditorCommand { final newNode = ParagraphNode( id: newNodeId, text: endText, - metadata: - isNewNodeABlockquote ? {'blockType': blockquoteAttribution} : {}, + metadata: isNewNodeABlockquote ? {'blockType': blockquoteAttribution} : {}, ); // Insert the new node after the current node. @@ -207,22 +196,16 @@ Widget? blockquoteBuilder(ComponentContext componentContext) { return null; } - final textSelection = - componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && - (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); return BlockquoteComponent( textKey: componentContext.componentKey, text: blockquoteNode.text, styleBuilder: componentContext.extensions[textStylesExtensionKey], textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, ); } diff --git a/super_editor/lib/src/default_editor/box_component.dart b/super_editor/lib/src/default_editor/box_component.dart index bb81ef0373..1f9a8e7600 100644 --- a/super_editor/lib/src/default_editor/box_component.dart +++ b/super_editor/lib/src/default_editor/box_component.dart @@ -37,15 +37,13 @@ class _BoxComponentState extends State with DocumentComponent { } @override - BinaryNodePosition? movePositionLeft(dynamic currentPosition, - [Set? movementModifiers]) { + BinaryNodePosition? movePositionLeft(dynamic currentPosition, [Set? movementModifiers]) { // BoxComponents don't support internal movement. return null; } @override - BinaryNodePosition? movePositionRight(dynamic currentPosition, - [Set? movementModifiers]) { + BinaryNodePosition? movePositionRight(dynamic currentPosition, [Set? movementModifiers]) { // BoxComponents don't support internal movement. return null; } @@ -65,8 +63,7 @@ class _BoxComponentState extends State with DocumentComponent { @override BinarySelection getCollapsedSelectionAt(nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception( - 'The given nodePosition ($nodePosition) is not compatible with BoxComponent'); + throw Exception('The given nodePosition ($nodePosition) is not compatible with BoxComponent'); } return const BinarySelection.all(); @@ -90,8 +87,7 @@ class _BoxComponentState extends State with DocumentComponent { @override Offset getOffsetForPosition(nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception( - 'Expected nodePosition of type BinaryPosition but received: $nodePosition'); + throw Exception('Expected nodePosition of type BinaryPosition but received: $nodePosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -101,8 +97,7 @@ class _BoxComponentState extends State with DocumentComponent { @override Rect getRectForPosition(dynamic nodePosition) { if (nodePosition is! BinaryNodePosition) { - throw Exception( - 'Expected nodePosition of type BinaryPosition but received: $nodePosition'); + throw Exception('Expected nodePosition of type BinaryPosition but received: $nodePosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -112,12 +107,10 @@ class _BoxComponentState extends State with DocumentComponent { @override Rect getRectForSelection(dynamic basePosition, dynamic extentPosition) { if (basePosition is! BinaryNodePosition) { - throw Exception( - 'Expected nodePosition of type BinaryPosition but received: $basePosition'); + throw Exception('Expected nodePosition of type BinaryPosition but received: $basePosition'); } if (extentPosition is! BinaryNodePosition) { - throw Exception( - 'Expected nodePosition of type BinaryPosition but received: $extentPosition'); + throw Exception('Expected nodePosition of type BinaryPosition but received: $extentPosition'); } final myBox = context.findRenderObject() as RenderBox; @@ -130,23 +123,19 @@ class _BoxComponentState extends State with DocumentComponent { } @override - BinarySelection getSelectionBetween( - {required basePosition, required extentPosition}) { + BinarySelection getSelectionBetween({required basePosition, required extentPosition}) { if (basePosition is! BinaryNodePosition) { - throw Exception( - 'The given basePosition ($basePosition) is not compatible with BoxComponent'); + throw Exception('The given basePosition ($basePosition) is not compatible with BoxComponent'); } if (extentPosition is! BinaryNodePosition) { - throw Exception( - 'The given extentPosition ($extentPosition) is not compatible with BoxComponent'); + throw Exception('The given extentPosition ($extentPosition) is not compatible with BoxComponent'); } return const BinarySelection.all(); } @override - BinarySelection getSelectionInRange( - Offset localBaseOffset, Offset localExtentOffset) { + BinarySelection getSelectionInRange(Offset localBaseOffset, Offset localExtentOffset) { return const BinarySelection.all(); } @@ -172,9 +161,7 @@ class BinaryNodePosition implements NodePosition { @override bool operator ==(Object other) => identical(this, other) || - other is BinaryNodePosition && - runtimeType == other.runtimeType && - isIncluded == other.isIncluded; + other is BinaryNodePosition && runtimeType == other.runtimeType && isIncluded == other.isIncluded; @override int get hashCode => isIncluded.hashCode; @@ -190,8 +177,7 @@ class BinaryNodePosition implements NodePosition { /// "selection" type. class BinarySelection implements NodeSelection { const BinarySelection.all() : position = const BinaryNodePosition.included(); - const BinarySelection.none() - : position = const BinaryNodePosition.notIncluded(); + const BinarySelection.none() : position = const BinaryNodePosition.notIncluded(); final BinaryNodePosition position; @@ -203,9 +189,7 @@ class BinarySelection implements NodeSelection { @override bool operator ==(Object other) => identical(this, other) || - other is BinarySelection && - runtimeType == other.runtimeType && - position == other.position; + other is BinarySelection && runtimeType == other.runtimeType && position == other.position; @override int get hashCode => position.hashCode; diff --git a/super_editor/lib/src/default_editor/common_editor_operations.dart b/super_editor/lib/src/default_editor/common_editor_operations.dart index cef7012c46..0753bd267a 100644 --- a/super_editor/lib/src/default_editor/common_editor_operations.dart +++ b/super_editor/lib/src/default_editor/common_editor_operations.dart @@ -61,8 +61,7 @@ class CommonEditorOperations { return false; } - composer.selection = - DocumentSelection.collapsed(position: documentPosition); + composer.selection = DocumentSelection.collapsed(position: documentPosition); return true; } @@ -83,11 +82,9 @@ class CommonEditorOperations { }) { DocumentPosition? position; if (findNearestPosition) { - position = documentLayoutResolver() - .getDocumentPositionNearestToOffset(documentOffset); + position = documentLayoutResolver().getDocumentPositionNearestToOffset(documentOffset); } else { - position = - documentLayoutResolver().getDocumentPositionAtOffset(documentOffset); + position = documentLayoutResolver().getDocumentPositionAtOffset(documentOffset); } if (position != null) { @@ -140,8 +137,7 @@ class CommonEditorOperations { return false; } - final selectedNode = - editor.document.getNodeById(composer.selection!.extent.nodeId); + final selectedNode = editor.document.getNodeById(composer.selection!.extent.nodeId); if (selectedNode is! TextNode) { return false; } @@ -149,8 +145,7 @@ class CommonEditorOperations { final docSelection = composer.selection!; final currentSelection = TextSelection( baseOffset: (docSelection.base.nodePosition as TextNodePosition).offset, - extentOffset: - (docSelection.extent.nodePosition as TextNodePosition).offset, + extentOffset: (docSelection.extent.nodePosition as TextNodePosition).offset, ); final selectedText = currentSelection.textInside(selectedNode.text.text); @@ -161,12 +156,9 @@ class CommonEditorOperations { final wordTextSelection = expandPositionToWord( text: selectedNode.text.text, - textPosition: TextPosition( - offset: - (docSelection.extent.nodePosition as TextNodePosition).offset), + textPosition: TextPosition(offset: (docSelection.extent.nodePosition as TextNodePosition).offset), ); - final wordNodeSelection = - TextNodeSelection.fromTextSelection(wordTextSelection); + final wordNodeSelection = TextNodeSelection.fromTextSelection(wordTextSelection); composer.selection = DocumentSelection( base: DocumentPosition( @@ -198,8 +190,7 @@ class CommonEditorOperations { return false; } - final selectedNode = - editor.document.getNodeById(composer.selection!.extent.nodeId); + final selectedNode = editor.document.getNodeById(composer.selection!.extent.nodeId); if (selectedNode is! TextNode) { return false; } @@ -207,8 +198,7 @@ class CommonEditorOperations { final docSelection = composer.selection!; final currentSelection = TextSelection( baseOffset: (docSelection.base.nodePosition as TextNodePosition).offset, - extentOffset: - (docSelection.extent.nodePosition as TextNodePosition).offset, + extentOffset: (docSelection.extent.nodePosition as TextNodePosition).offset, ); final selectedText = currentSelection.textInside(selectedNode.text.text); @@ -219,12 +209,9 @@ class CommonEditorOperations { final paragraphTextSelection = expandPositionToParagraph( text: selectedNode.text.text, - textPosition: TextPosition( - offset: - (docSelection.extent.nodePosition as TextNodePosition).offset), + textPosition: TextPosition(offset: (docSelection.extent.nodePosition as TextNodePosition).offset), ); - final paragraphNodeSelection = - TextNodeSelection.fromTextSelection(paragraphTextSelection); + final paragraphNodeSelection = TextNodeSelection.fromTextSelection(paragraphTextSelection); composer.selection = DocumentSelection( base: DocumentPosition( @@ -302,8 +289,7 @@ class CommonEditorOperations { } if (!composer.selection!.isCollapsed && !expand) { - composer.selection = - composer.selection!.collapseUpstream(editor.document); + composer.selection = composer.selection!.collapseUpstream(editor.document); return true; } @@ -313,15 +299,13 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = - documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionLeft( - currentExtent.nodePosition, movementModifiers); + dynamic newExtentNodePosition = extentComponent.movePositionLeft(currentExtent.nodePosition, movementModifiers); if (newExtentNodePosition == null) { // Move to next node @@ -333,8 +317,7 @@ class CommonEditorOperations { } newExtentNodeId = nextNode.id; - final nextComponent = - documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } @@ -384,8 +367,7 @@ class CommonEditorOperations { } if (!composer.selection!.isCollapsed && !expand) { - composer.selection = - composer.selection!.collapseDownstream(editor.document); + composer.selection = composer.selection!.collapseDownstream(editor.document); return true; } @@ -395,15 +377,13 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = - documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = extentComponent.movePositionRight( - currentExtent.nodePosition, movementModifiers); + dynamic newExtentNodePosition = extentComponent.movePositionRight(currentExtent.nodePosition, movementModifiers); if (newExtentNodePosition == null) { // Move to next node @@ -416,8 +396,7 @@ class CommonEditorOperations { } newExtentNodeId = nextNode.id; - final nextComponent = - documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { throw Exception( 'Could not find next component to move the selection horizontally. Next node ID: ${nextNode.id}'); @@ -475,30 +454,25 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = - documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = - extentComponent.movePositionUp(currentExtent.nodePosition); + dynamic newExtentNodePosition = extentComponent.movePositionUp(currentExtent.nodePosition); if (newExtentNodePosition == null) { // Move to next node final nextNode = editor.document.getNodeBefore(node); if (nextNode != null) { newExtentNodeId = nextNode.id; - final nextComponent = - documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } - final offsetToMatch = - extentComponent.getOffsetForPosition(currentExtent.nodePosition); - newExtentNodePosition = - nextComponent.getEndPositionNearX(offsetToMatch.dx); + final offsetToMatch = extentComponent.getOffsetForPosition(currentExtent.nodePosition); + newExtentNodePosition = nextComponent.getEndPositionNearX(offsetToMatch.dx); } else { // We're at the top of the document. Move the cursor to the // beginning of the current node. @@ -556,30 +530,25 @@ class CommonEditorOperations { if (node == null) { return false; } - final extentComponent = - documentLayoutResolver().getComponentByNodeId(nodeId); + final extentComponent = documentLayoutResolver().getComponentByNodeId(nodeId); if (extentComponent == null) { return false; } String newExtentNodeId = nodeId; - dynamic newExtentNodePosition = - extentComponent.movePositionDown(currentExtent.nodePosition); + dynamic newExtentNodePosition = extentComponent.movePositionDown(currentExtent.nodePosition); if (newExtentNodePosition == null) { // Move to next node final nextNode = editor.document.getNodeAfter(node); if (nextNode != null) { newExtentNodeId = nextNode.id; - final nextComponent = - documentLayoutResolver().getComponentByNodeId(nextNode.id); + final nextComponent = documentLayoutResolver().getComponentByNodeId(nextNode.id); if (nextComponent == null) { return false; } - final offsetToMatch = - extentComponent.getOffsetForPosition(currentExtent.nodePosition); - newExtentNodePosition = - nextComponent.getBeginningPositionNearX(offsetToMatch.dx); + final offsetToMatch = extentComponent.getOffsetForPosition(currentExtent.nodePosition); + newExtentNodePosition = nextComponent.getBeginningPositionNearX(offsetToMatch.dx); } else { // We're at the bottom of the document. Move the cursor to the // end of the current node. @@ -621,20 +590,14 @@ class CommonEditorOperations { return false; } - if (!composer.selection!.isCollapsed || - composer.selection!.extent.nodePosition is BinaryNodePosition) { + if (!composer.selection!.isCollapsed || composer.selection!.extent.nodePosition is BinaryNodePosition) { // A span of content is selected. Delete the selection. return deleteSelection(); } else if (composer.selection!.extent.nodePosition is TextNodePosition) { - final textPosition = - composer.selection!.extent.nodePosition as TextNodePosition; - final text = (editor.document - .getNodeById(composer.selection!.extent.nodeId) as TextNode) - .text - .text; + final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; + final text = (editor.document.getNodeById(composer.selection!.extent.nodeId) as TextNode).text.text; if (textPosition.offset == text.length) { - final node = - editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final node = editor.document.getNodeById(composer.selection!.extent.nodeId)!; final nodeAfter = editor.document.getNodeAfter(node); if (nodeAfter is TextNode) { @@ -722,27 +685,21 @@ class CommonEditorOperations { if (composer.selection == null) { return false; } - if (!_isTextEntryNode( - document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { return false; } - if (composer.selection!.isCollapsed && - (composer.selection!.extent.nodePosition as TextNodePosition).offset <= - 0) { + if (composer.selection!.isCollapsed && (composer.selection!.extent.nodePosition as TextNodePosition).offset <= 0) { return false; } - final textNode = - editor.document.getNode(composer.selection!.extent) as TextNode; + final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; final text = textNode.text; - final currentTextPosition = - (composer.selection!.extent.nodePosition as TextNodePosition); + final currentTextPosition = (composer.selection!.extent.nodePosition as TextNodePosition); if (currentTextPosition.offset >= text.text.length) { return false; } - final nextCharacterOffset = - getCharacterEndBounds(text.text, currentTextPosition.offset); + final nextCharacterOffset = getCharacterEndBounds(text.text, currentTextPosition.offset); // Delete the selected content. editor.executeCommand( @@ -777,25 +734,20 @@ class CommonEditorOperations { return false; } - if (!composer.selection!.isCollapsed || - composer.selection!.extent.nodePosition is BinaryNodePosition) { + if (!composer.selection!.isCollapsed || composer.selection!.extent.nodePosition is BinaryNodePosition) { // A span of content is selected. Delete the selection. return deleteSelection(); } - final node = - editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final node = editor.document.getNodeById(composer.selection!.extent.nodeId)!; // If the caret is at the beginning of a list item, unindent the list item. - if (node is ListItemNode && - (composer.selection!.extent.nodePosition as TextNodePosition).offset == - 0) { + if (node is ListItemNode && (composer.selection!.extent.nodePosition as TextNodePosition).offset == 0) { return unindentListItem(); } if (composer.selection!.extent.nodePosition is TextNodePosition) { - final textPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; if (textPosition.offset == 0) { final nodeBefore = editor.document.getNodeBefore(node); @@ -890,23 +842,17 @@ class CommonEditorOperations { if (composer.selection == null) { return false; } - if (!_isTextEntryNode( - document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { return false; } - if (composer.selection!.isCollapsed && - (composer.selection!.extent.nodePosition as TextNodePosition).offset <= - 0) { + if (composer.selection!.isCollapsed && (composer.selection!.extent.nodePosition as TextNodePosition).offset <= 0) { return false; } - final textNode = - editor.document.getNode(composer.selection!.extent) as TextNode; - final currentTextPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; + final currentTextPosition = composer.selection!.extent.nodePosition as TextNodePosition; - final previousCharacterOffset = - getCharacterStartBounds(textNode.text.text, currentTextPosition.offset); + final previousCharacterOffset = getCharacterStartBounds(textNode.text.text, currentTextPosition.offset); final newSelectionPosition = DocumentPosition( nodeId: textNode.id, @@ -929,8 +875,7 @@ class CommonEditorOperations { ), ); - composer.selection = - DocumentSelection.collapsed(position: newSelectionPosition); + composer.selection = DocumentSelection.collapsed(position: newSelectionPosition); return true; } @@ -948,8 +893,7 @@ class CommonEditorOperations { if (composer.selection!.extent.nodePosition is! BinaryNodePosition) { return false; } - if (!(composer.selection!.extent.nodePosition as BinaryNodePosition) - .isIncluded) { + if (!(composer.selection!.extent.nodePosition as BinaryNodePosition).isIncluded) { return false; } @@ -1004,11 +948,9 @@ class CommonEditorOperations { throw Exception( 'Tried to access document node at index $newSelectionNodeIndex but the document returned null.'); } - final component = - documentLayout.getComponentByNodeId(newSelectionNode.id); + final component = documentLayout.getComponentByNodeId(newSelectionNode.id); if (component == null) { - throw Exception( - 'Couldn\'t find editor component for node: ${newSelectionNode.id}'); + throw Exception('Couldn\'t find editor component for node: ${newSelectionNode.id}'); } return DocumentPosition( nodeId: newSelectionNode.id, @@ -1020,14 +962,11 @@ class CommonEditorOperations { // is now the first node in the document. final newSelectionNode = document.getNodeAt(0); if (newSelectionNode == null) { - throw Exception( - 'Could not obtain the first node in a non-empty document.'); + throw Exception('Could not obtain the first node in a non-empty document.'); } - final component = - documentLayout.getComponentByNodeId(newSelectionNode.id); + final component = documentLayout.getComponentByNodeId(newSelectionNode.id); if (component == null) { - throw Exception( - 'Couldn\'t find editor component for node: ${newSelectionNode.id}'); + throw Exception('Couldn\'t find editor component for node: ${newSelectionNode.id}'); } return DocumentPosition( nodeId: newSelectionNode.id, @@ -1050,8 +989,7 @@ class CommonEditorOperations { DeleteSelectionCommand(documentSelection: composer.selection!), ); - composer.selection = - DocumentSelection.collapsed(position: newSelectionPosition); + composer.selection = DocumentSelection.collapsed(position: newSelectionPosition); } DocumentPosition _getDocumentPositionAfterDeletion({ @@ -1066,16 +1004,14 @@ class CommonEditorOperations { final basePosition = selection.base; final baseNode = document.getNode(basePosition); if (baseNode == null) { - throw Exception( - 'Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); + throw Exception('Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); } final baseNodeIndex = document.getNodeIndex(baseNode); final extentPosition = selection.extent; final extentNode = document.getNode(extentPosition); if (extentNode == null) { - throw Exception( - 'Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); + throw Exception('Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); } final extentNodeIndex = document.getNodeIndex(extentNode); DocumentPosition newSelectionPosition; @@ -1083,8 +1019,7 @@ class CommonEditorOperations { if (baseNodeIndex != extentNodeIndex) { // Place the caret at the current position within the // first node in the selection. - newSelectionPosition = - baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; + newSelectionPosition = baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; // If it's a binary selection node then that node will // be replaced by a ParagraphNode with the same ID. @@ -1108,10 +1043,8 @@ class CommonEditorOperations { nodePosition: const TextNodePosition(offset: 0), ); } else if (basePosition.nodePosition is TextNodePosition) { - final baseOffset = - (basePosition.nodePosition as TextNodePosition).offset; - final extentOffset = - (extentPosition.nodePosition as TextNodePosition).offset; + final baseOffset = (basePosition.nodePosition as TextNodePosition).offset; + final extentOffset = (extentPosition.nodePosition as TextNodePosition).offset; newSelectionPosition = DocumentPosition( nodeId: baseNode.id, @@ -1249,10 +1182,8 @@ class CommonEditorOperations { return false; } - final baseNode = - editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = - editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } @@ -1266,10 +1197,8 @@ class CommonEditorOperations { deleteSelection(); } - final textNode = - editor.document.getNode(composer.selection!.extent) as TextNode; - final initialTextOffset = - (composer.selection!.extent.nodePosition as TextNodePosition).offset; + final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; + final initialTextOffset = (composer.selection!.extent.nodePosition as TextNodePosition).offset; editor.executeCommand( InsertTextCommand( @@ -1339,26 +1268,20 @@ class CommonEditorOperations { } final text = node.text; - final textSelection = - composer.selection!.extent.nodePosition as TextNodePosition; + final textSelection = composer.selection!.extent.nodePosition as TextNodePosition; final textBeforeCaret = text.text.substring(0, textSelection.offset); final unorderedListItemMatch = RegExp(r'^\s*[\*-]\s+$'); - final hasUnorderedListItemMatch = - unorderedListItemMatch.hasMatch(textBeforeCaret); + final hasUnorderedListItemMatch = unorderedListItemMatch.hasMatch(textBeforeCaret); final orderedListItemMatch = RegExp(r'^\s*[1].*\s+$'); - final hasOrderedListItemMatch = - orderedListItemMatch.hasMatch(textBeforeCaret); + final hasOrderedListItemMatch = orderedListItemMatch.hasMatch(textBeforeCaret); - _log.log('_convertParagraphIfDesired', - ' - text before caret: "$textBeforeCaret"'); + _log.log('_convertParagraphIfDesired', ' - text before caret: "$textBeforeCaret"'); if (hasUnorderedListItemMatch || hasOrderedListItemMatch) { - _log.log( - '_convertParagraphIfDesired', ' - found unordered list item prefix'); + _log.log('_convertParagraphIfDesired', ' - found unordered list item prefix'); int startOfNewText = textBeforeCaret.length; - while (startOfNewText < node.text.text.length && - node.text.text[startOfNewText] == ' ') { + while (startOfNewText < node.text.text.length && node.text.text[startOfNewText] == ' ') { startOfNewText += 1; } final adjustedText = node.text.copyText(startOfNewText); @@ -1377,13 +1300,11 @@ class CommonEditorOperations { // We removed some text at the beginning of the list item. // Move the selection back by that same amount. - final textPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: node.id, - nodePosition: - TextNodePosition(offset: textPosition.offset - startOfNewText), + nodePosition: TextNodePosition(offset: textPosition.offset - startOfNewText), ), ); @@ -1409,8 +1330,7 @@ class CommonEditorOperations { }), ); - node.text = node.text.removeRegion( - startOffset: 0, endOffset: hrMatch.firstMatch(textBeforeCaret)!.end); + node.text = node.text.removeRegion(startOffset: 0, endOffset: hrMatch.firstMatch(textBeforeCaret)!.end); composer.selection = DocumentSelection.collapsed( position: DocumentPosition( @@ -1426,8 +1346,7 @@ class CommonEditorOperations { final hasBlockquoteMatch = blockquoteMatch.hasMatch(textBeforeCaret); if (hasBlockquoteMatch) { int startOfNewText = textBeforeCaret.length; - while (startOfNewText < node.text.text.length && - node.text.text[startOfNewText] == ' ') { + while (startOfNewText < node.text.text.length && node.text.text[startOfNewText] == ' ') { startOfNewText += 1; } final adjustedText = node.text.copyText(startOfNewText); @@ -1448,13 +1367,11 @@ class CommonEditorOperations { // We removed some text at the beginning of the list item. // Move the selection back by that same amount. - final textPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final textPosition = composer.selection!.extent.nodePosition as TextNodePosition; composer.selection = DocumentSelection.collapsed( position: DocumentPosition( nodeId: node.id, - nodePosition: - TextNodePosition(offset: textPosition.offset - startOfNewText), + nodePosition: TextNodePosition(offset: textPosition.offset - startOfNewText), ), ); @@ -1467,18 +1384,13 @@ class CommonEditorOperations { options: const LinkifyOptions( humanize: false, )); - final int linkCount = extractedLinks.fold( - 0, (value, element) => element is UrlElement ? value + 1 : value); - final String nonEmptyText = extractedLinks.fold( - '', - (value, element) => - element is TextElement ? value + element.text.trim() : value); + final int linkCount = extractedLinks.fold(0, (value, element) => element is UrlElement ? value + 1 : value); + final String nonEmptyText = + extractedLinks.fold('', (value, element) => element is TextElement ? value + element.text.trim() : value); if (linkCount == 1 && nonEmptyText.isEmpty) { // This node's text is just a URL, try to interpret it // as a known type. - final link = extractedLinks - .firstWhereOrNull((element) => element is UrlElement)! - .text; + final link = extractedLinks.firstWhereOrNull((element) => element is UrlElement)!.text; _processUrlNode( document: editor.document, editor: editor, @@ -1503,8 +1415,7 @@ class CommonEditorOperations { final response = await http.get(Uri.parse(url)); if (response.statusCode < 200 || response.statusCode >= 300) { - _log.log('_processUrlNode', - 'Failed to load URL: ${response.statusCode} - ${response.reasonPhrase}'); + _log.log('_processUrlNode', 'Failed to load URL: ${response.statusCode} - ${response.reasonPhrase}'); return; } @@ -1519,18 +1430,16 @@ class CommonEditorOperations { } // The URL is an image. Convert the node. - _log.log('_processUrlNode', - 'The URL is an image. Converting the ParagraphNode to an ImageNode.'); + _log.log('_processUrlNode', 'The URL is an image. Converting the ParagraphNode to an ImageNode.'); final node = document.getNodeById(nodeId); if (node is! ParagraphNode) { - _log.log('_processUrlNode', - 'The node has become something other than a ParagraphNode ($node). Can\'t convert ndoe.'); + _log.log( + '_processUrlNode', 'The node has become something other than a ParagraphNode ($node). Can\'t convert ndoe.'); return; } final currentText = node.text.text; if (currentText.trim() != originalText.trim()) { - _log.log('_processUrlNode', - 'The node content changed in a non-trivial way. Aborting node conversion.'); + _log.log('_processUrlNode', 'The node content changed in a non-trivial way. Aborting node conversion.'); return; } @@ -1566,23 +1475,18 @@ class CommonEditorOperations { if (!composer.selection!.isCollapsed) { return false; } - if (!_isTextEntryNode( - document: editor.document, selection: composer.selection!)) { + if (!_isTextEntryNode(document: editor.document, selection: composer.selection!)) { return false; } - final textNode = - editor.document.getNode(composer.selection!.extent) as TextNode; - final initialTextOffset = - (composer.selection!.extent.nodePosition as TextNodePosition).offset; + final textNode = editor.document.getNode(composer.selection!.extent) as TextNode; + final initialTextOffset = (composer.selection!.extent.nodePosition as TextNodePosition).offset; editor.executeCommand( InsertTextCommand( documentPosition: composer.selection!.extent, textToInsert: character, - attributions: ignoreComposerAttributions - ? {} - : composer.preferences.currentAttributions, + attributions: ignoreComposerAttributions ? {} : composer.preferences.currentAttributions, ), ); @@ -1621,10 +1525,8 @@ class CommonEditorOperations { } // Ensure that the entire selection sits within the same node. - final baseNode = - editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = - editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } @@ -1647,16 +1549,14 @@ class CommonEditorOperations { editor.executeCommand( SplitListItemCommand( nodeId: extentNode.id, - splitPosition: - composer.selection!.extent.nodePosition as TextNodePosition, + splitPosition: composer.selection!.extent.nodePosition as TextNodePosition, newNodeId: newNodeId, ), ); } else if (extentNode is ParagraphNode) { // Split the paragraph into two. This includes headers, blockquotes, and // any other block-level paragraph. - final currentExtentPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final currentExtentPosition = composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = extentNode.endPosition; editor.executeCommand( @@ -1664,8 +1564,7 @@ class CommonEditorOperations { nodeId: extentNode.id, splitPosition: currentExtentPosition, newNodeId: newNodeId, - replicateExistingMetdata: - currentExtentPosition.offset != endOfParagraph.offset, + replicateExistingMetdata: currentExtentPosition.offset != endOfParagraph.offset, ), ); } else { @@ -1727,8 +1626,7 @@ class CommonEditorOperations { editor.executeCommand( EditorCommandFunction((document, transaction) { - final paragraphPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final paragraphPosition = composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = node.endPosition; final nodeIndex = editor.document.getNodeIndex(node); @@ -1750,8 +1648,7 @@ class CommonEditorOperations { ); } else if (paragraphPosition == endOfParagraph) { // Insert HR after the paragraph. - final imageNode = - ImageNode(id: DocumentEditor.createNodeId(), imageUrl: url); + final imageNode = ImageNode(id: DocumentEditor.createNodeId(), imageUrl: url); transaction.insertNodeAfter(previousNode: node, newNode: imageNode); @@ -1767,8 +1664,7 @@ class CommonEditorOperations { final textAfter = node.text.copyText(paragraphPosition.offset); final imageNode = ImageNode(id: nodeId, imageUrl: url); - final newParagraph = - ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); + final newParagraph = ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); // TODO: node operations need to be a part of a transaction, somehow. node.text = textBefore; @@ -1827,8 +1723,7 @@ class CommonEditorOperations { editor.executeCommand( EditorCommandFunction((document, transaction) { - final paragraphPosition = - composer.selection!.extent.nodePosition as TextNodePosition; + final paragraphPosition = composer.selection!.extent.nodePosition as TextNodePosition; final endOfParagraph = node.endPosition; DocumentSelection newSelection; @@ -1864,8 +1759,7 @@ class CommonEditorOperations { final textAfter = node.text.copyText(paragraphPosition.offset); final hrNode = HorizontalRuleNode(id: DocumentEditor.createNodeId()); - final newParagraph = - ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); + final newParagraph = ParagraphNode(id: DocumentEditor.createNodeId(), text: textAfter); // TODO: node operations need to be a part of a transaction, somehow. node.text = textBefore; @@ -1899,10 +1793,8 @@ class CommonEditorOperations { return false; } - final baseNode = - editor.document.getNodeById(composer.selection!.base.nodeId); - final extentNode = - editor.document.getNodeById(composer.selection!.extent.nodeId); + final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId); + final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId); if (baseNode is! ListItemNode || extentNode is! ListItemNode) { return false; } @@ -1928,10 +1820,8 @@ class CommonEditorOperations { return false; } - final baseNode = - editor.document.getNodeById(composer.selection!.base.nodeId); - final extentNode = - editor.document.getNodeById(composer.selection!.extent.nodeId); + final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId); + final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId); if (baseNode!.id != extentNode!.id) { return false; } @@ -2007,8 +1897,7 @@ class CommonEditorOperations { } final nodeIndex = editor.document.getNodeIndex(node); - final newNode = ParagraphNode( - id: nodeId, metadata: {'blockType': blockquoteAttribution}, text: text); + final newNode = ParagraphNode(id: nodeId, metadata: {'blockType': blockquoteAttribution}, text: text); editor.executeCommand( EditorCommandFunction((document, transaction) { @@ -2039,18 +1928,15 @@ class CommonEditorOperations { return false; } - final baseNode = - editor.document.getNodeById(composer.selection!.base.nodeId)!; - final extentNode = - editor.document.getNodeById(composer.selection!.extent.nodeId)!; + final baseNode = editor.document.getNodeById(composer.selection!.base.nodeId)!; + final extentNode = editor.document.getNodeById(composer.selection!.extent.nodeId)!; if (baseNode.id != extentNode.id) { return false; } if (extentNode is! TextNode) { return false; } - if (extentNode is ParagraphNode && - !extentNode.metadata.containsKey('blockType')) { + if (extentNode is ParagraphNode && !extentNode.metadata.containsKey('blockType')) { // This content is already a regular paragraph. return false; } diff --git a/super_editor/lib/src/default_editor/document_interaction.dart b/super_editor/lib/src/default_editor/document_interaction.dart index 26a7f9fc23..957c49a500 100644 --- a/super_editor/lib/src/default_editor/document_interaction.dart +++ b/super_editor/lib/src/default_editor/document_interaction.dart @@ -66,8 +66,7 @@ class DocumentInteractor extends StatefulWidget { _DocumentInteractorState createState() => _DocumentInteractorState(); } -class _DocumentInteractorState extends State - with SingleTickerProviderStateMixin { +class _DocumentInteractorState extends State with SingleTickerProviderStateMixin { final _dragGutterExtent = 100; final _maxDragSpeed = 20; @@ -98,8 +97,7 @@ class _DocumentInteractorState extends State _focusNode = widget.focusNode ?? FocusNode(); _ticker = createTicker(_onTick); _scrollController = - _scrollController = (widget.scrollController ?? ScrollController()) - ..addListener(_updateDragSelection); + _scrollController = (widget.scrollController ?? ScrollController())..addListener(_updateDragSelection); widget.editContext.composer.addListener(_onSelectionChange); } @@ -116,8 +114,7 @@ class _DocumentInteractorState extends State if (oldWidget.scrollController == null) { _scrollController.dispose(); } - _scrollController = (widget.scrollController ?? ScrollController()) - ..addListener(_updateDragSelection); + _scrollController = (widget.scrollController ?? ScrollController())..addListener(_updateDragSelection); } if (widget.focusNode != oldWidget.focusNode) { _focusNode = widget.focusNode ?? FocusNode(); @@ -153,8 +150,7 @@ class _DocumentInteractorState extends State } void _ensureSelectionExtentIsVisible() { - _log.log('_ensureSelectionExtentIsVisible', - 'selection: ${widget.editContext.composer.selection}'); + _log.log('_ensureSelectionExtentIsVisible', 'selection: ${widget.editContext.composer.selection}'); final selection = widget.editContext.composer.selection; if (selection == null) { return; @@ -174,30 +170,20 @@ class _DocumentInteractorState extends State } final myBox = context.findRenderObject() as RenderBox; - final beyondTopExtent = - min(extentRect.top - _scrollController.offset - _dragGutterExtent, 0) - .abs(); - final beyondBottomExtent = max( - extentRect.bottom - - myBox.size.height - - _scrollController.offset + - _dragGutterExtent, - 0); + final beyondTopExtent = min(extentRect.top - _scrollController.offset - _dragGutterExtent, 0).abs(); + final beyondBottomExtent = + max(extentRect.bottom - myBox.size.height - _scrollController.offset + _dragGutterExtent, 0); _log.log('_ensureSelectionExtentIsVisible', 'Ensuring extent is visible.'); - _log.log('_ensureSelectionExtentIsVisible', - ' - interaction size: ${myBox.size}'); - _log.log('_ensureSelectionExtentIsVisible', - ' - scroll extent: ${_scrollController.offset}'); + _log.log('_ensureSelectionExtentIsVisible', ' - interaction size: ${myBox.size}'); + _log.log('_ensureSelectionExtentIsVisible', ' - scroll extent: ${_scrollController.offset}'); _log.log('_ensureSelectionExtentIsVisible', ' - extent rect: $extentRect'); - _log.log( - '_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); - _log.log('_ensureSelectionExtentIsVisible', - ' - beyond bottom: $beyondBottomExtent'); + _log.log('_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); + _log.log('_ensureSelectionExtentIsVisible', ' - beyond bottom: $beyondBottomExtent'); if (beyondTopExtent > 0) { - final newScrollPosition = (_scrollController.offset - beyondTopExtent) - .clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollPosition = + (_scrollController.offset - beyondTopExtent).clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.animateTo( newScrollPosition, @@ -205,8 +191,8 @@ class _DocumentInteractorState extends State curve: Curves.easeOut, ); } else if (beyondBottomExtent > 0) { - final newScrollPosition = (beyondBottomExtent + _scrollController.offset) - .clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollPosition = + (beyondBottomExtent + _scrollController.offset).clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.animateTo( newScrollPosition, @@ -225,8 +211,7 @@ class _DocumentInteractorState extends State ExecutionInstruction instruction = ExecutionInstruction.continueExecution; int index = 0; - while (instruction == ExecutionInstruction.continueExecution && - index < widget.keyboardActions.length) { + while (instruction == ExecutionInstruction.continueExecution && index < widget.keyboardActions.length) { instruction = widget.keyboardActions[index]( editContext: widget.editContext, keyEvent: keyEvent, @@ -234,9 +219,7 @@ class _DocumentInteractorState extends State index += 1; } - return instruction == ExecutionInstruction.haltExecution - ? KeyEventResult.handled - : KeyEventResult.ignored; + return instruction == ExecutionInstruction.haltExecution ? KeyEventResult.handled : KeyEventResult.ignored; } void _onTapDown(TapDownDetails details) { @@ -322,8 +305,7 @@ class _DocumentInteractorState extends State _dragStartInDoc = _getDocOffset(_dragStartInViewport!); _clearSelection(); - _dragRectInViewport = - Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); + _dragRectInViewport = Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); _focusNode.requestFocus(); } @@ -333,8 +315,7 @@ class _DocumentInteractorState extends State setState(() { _dragEndInViewport = details.localPosition; _dragEndInDoc = _getDocOffset(_dragEndInViewport!); - _dragRectInViewport = - Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); + _dragRectInViewport = Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); _log.log('_onPanUpdate', ' - drag rect: $_dragRectInViewport'); _updateCursorStyle(details.localPosition); _updateDragSelection(); @@ -373,8 +354,7 @@ class _DocumentInteractorState extends State required DocumentPosition docPosition, required DocumentLayout docLayout, }) { - final newSelection = - getWordSelection(docPosition: docPosition, docLayout: docLayout); + final newSelection = getWordSelection(docPosition: docPosition, docLayout: docLayout); if (newSelection != null) { widget.editContext.composer.selection = newSelection; return true; @@ -387,8 +367,7 @@ class _DocumentInteractorState extends State required DocumentPosition docPosition, required DocumentLayout docLayout, }) { - final newSelection = - getParagraphSelection(docPosition: docPosition, docLayout: docLayout); + final newSelection = getParagraphSelection(docPosition: docPosition, docLayout: docLayout); if (newSelection != null) { widget.editContext.composer.selection = newSelection; return true; @@ -425,14 +404,11 @@ class _DocumentInteractorState extends State required Offset extentOffset, required SelectionType selectionType, }) { - _log.log('_selectionRegion', - 'Composer: selectionRegion(). Mode: $selectionType'); - DocumentSelection? selection = - documentLayout.getDocumentSelectionInRegion(baseOffset, extentOffset); + _log.log('_selectionRegion', 'Composer: selectionRegion(). Mode: $selectionType'); + DocumentSelection? selection = documentLayout.getDocumentSelectionInRegion(baseOffset, extentOffset); DocumentPosition? basePosition = selection?.base; DocumentPosition? extentPosition = selection?.extent; - _log.log( - '_selectionRegion', ' - base: $basePosition, extent: $extentPosition'); + _log.log('_selectionRegion', ' - base: $basePosition, extent: $extentPosition'); if (basePosition == null || extentPosition == null) { widget.editContext.composer.selection = null; @@ -448,9 +424,7 @@ class _DocumentInteractorState extends State widget.editContext.composer.selection = null; return; } - basePosition = baseOffset.dy < extentOffset.dy - ? baseParagraphSelection.base - : baseParagraphSelection.extent; + basePosition = baseOffset.dy < extentOffset.dy ? baseParagraphSelection.base : baseParagraphSelection.extent; final extentParagraphSelection = getParagraphSelection( docPosition: extentPosition, @@ -460,9 +434,8 @@ class _DocumentInteractorState extends State widget.editContext.composer.selection = null; return; } - extentPosition = baseOffset.dy < extentOffset.dy - ? extentParagraphSelection.extent - : extentParagraphSelection.base; + extentPosition = + baseOffset.dy < extentOffset.dy ? extentParagraphSelection.extent : extentParagraphSelection.base; } else if (selectionType == SelectionType.word) { _log.log('_selectionRegion', ' - selecting a word'); final baseWordSelection = getWordSelection( @@ -490,8 +463,7 @@ class _DocumentInteractorState extends State base: basePosition, extent: extentPosition, )); - _log.log('_selectionRegion', - 'Region selection: ${widget.editContext.composer.selection}'); + _log.log('_selectionRegion', 'Region selection: ${widget.editContext.composer.selection}'); } void _clearSelection() { @@ -504,8 +476,7 @@ class _DocumentInteractorState extends State if (desiredCursor != null && desiredCursor != _cursorStyle.value) { _cursorStyle.value = desiredCursor; - } else if (desiredCursor == null && - _cursorStyle.value != SystemMouseCursors.basic) { + } else if (desiredCursor == null && _cursorStyle.value != SystemMouseCursors.basic) { _cursorStyle.value = SystemMouseCursors.basic; } } @@ -513,8 +484,7 @@ class _DocumentInteractorState extends State // Converts the given [offset] from the [DocumentInteractor]'s coordinate // space to the [DocumentLayout]'s coordinate space. Offset _getDocOffset(Offset offset) { - return _layout.getDocumentOffsetFromAncestorOffset( - offset, context.findRenderObject()!); + return _layout.getDocumentOffsetFromAncestorOffset(offset, context.findRenderObject()!); } // ------ scrolling ------- @@ -524,8 +494,8 @@ class _DocumentInteractorState extends State /// form of mouse scrolling. void _onPointerSignal(PointerSignalEvent event) { if (event is PointerScrollEvent) { - final newScrollOffset = (_scrollController.offset + event.scrollDelta.dy) - .clamp(0.0, _scrollController.position.maxScrollExtent); + final newScrollOffset = + (_scrollController.offset + event.scrollDelta.dy).clamp(0.0, _scrollController.position.maxScrollExtent); _scrollController.jumpTo(newScrollOffset); _updateDragSelection(); @@ -536,8 +506,7 @@ class _DocumentInteractorState extends State // - _dragEndInViewport must be non-null void _scrollIfNearBoundary() { if (_dragEndInViewport == null) { - _log.log('_scrollIfNearBoundary', - "Can't scroll near boundary because _dragEndInViewport is null"); + _log.log('_scrollIfNearBoundary', "Can't scroll near boundary because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -576,8 +545,7 @@ class _DocumentInteractorState extends State void _scrollUp() { if (_dragEndInViewport == null) { - _log.log( - '_scrollUp', "Can't scroll up because _dragEndInViewport is null"); + _log.log('_scrollUp', "Can't scroll up because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -613,20 +581,17 @@ class _DocumentInteractorState extends State void _scrollDown() { if (_dragEndInViewport == null) { - _log.log('_scrollDown', - "Can't scroll down because _dragEndInViewport is null"); + _log.log('_scrollDown', "Can't scroll down because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } - if (_scrollController.offset >= - _scrollController.position.maxScrollExtent) { + if (_scrollController.offset >= _scrollController.position.maxScrollExtent) { return; } final editorBox = context.findRenderObject() as RenderBox; - final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy) - .clamp(0.0, _dragGutterExtent); + final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy).clamp(0.0, _dragGutterExtent); final speedPercent = 1.0 - (gutterAmount / _dragGutterExtent); final scrollAmount = lerpDouble(0, _maxDragSpeed, speedPercent); @@ -654,9 +619,7 @@ class _DocumentInteractorState extends State document: widget.document, ), Positioned.fill( - child: widget.showDebugPaint - ? _buildDragSelection() - : const SizedBox(), + child: widget.showDebugPaint ? _buildDragSelection() : const SizedBox(), ), ], ), @@ -707,8 +670,7 @@ class _DocumentInteractorState extends State child: RawGestureDetector( behavior: HitTestBehavior.translucent, gestures: { - TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers< - TapSequenceGestureRecognizer>( + TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => TapSequenceGestureRecognizer(), (TapSequenceGestureRecognizer recognizer) { recognizer @@ -719,8 +681,7 @@ class _DocumentInteractorState extends State ..onTripleTap = _onTripleTap; }, ), - PanGestureRecognizer: - GestureRecognizerFactoryWithHandlers( + PanGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => PanGestureRecognizer(), (PanGestureRecognizer recognizer) { recognizer diff --git a/super_editor/lib/src/default_editor/document_keyboard_actions.dart b/super_editor/lib/src/default_editor/document_keyboard_actions.dart index 6bd5fd0ba1..b7ac444c30 100644 --- a/super_editor/lib/src/default_editor/document_keyboard_actions.dart +++ b/super_editor/lib/src/default_editor/document_keyboard_actions.dart @@ -33,8 +33,7 @@ ExecutionInstruction pasteWhenCmdVIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.character?.toLowerCase() != 'v') { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'v') { return ExecutionInstruction.continueExecution; } if (editContext.composer.selection == null) { @@ -53,12 +52,10 @@ ExecutionInstruction pasteWhenCmdVIsPressed({ // Delete the selected content. editContext.editor.executeCommand( - DeleteSelectionCommand( - documentSelection: editContext.composer.selection!), + DeleteSelectionCommand(documentSelection: editContext.composer.selection!), ); - editContext.composer.selection = - DocumentSelection.collapsed(position: pastePosition); + editContext.composer.selection = DocumentSelection.collapsed(position: pastePosition); } // TODO: figure out a general approach for asynchronous behaviors that @@ -77,15 +74,12 @@ ExecutionInstruction selectAllWhenCmdAIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.character?.toLowerCase() != 'a') { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'a') { return ExecutionInstruction.continueExecution; } final didSelectAll = editContext.commonOps.selectAll(); - return didSelectAll - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didSelectAll ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } Future _paste({ @@ -127,20 +121,16 @@ class _PasteEditorCommand implements EditorCommand { _log.log('_PasteEditorCommand', ' - "$piece"'); } - final currentNodeWithSelection = - document.getNodeById(_pastePosition.nodeId); + final currentNodeWithSelection = document.getNodeById(_pastePosition.nodeId); DocumentPosition? newSelectionPosition; if (currentNodeWithSelection is TextNode) { final textNode = document.getNode(_pastePosition) as TextNode; - final pasteTextOffset = - (_pastePosition.nodePosition as TextPosition).offset; - final attributionsAtPasteOffset = - textNode.text.getAllAttributionsAt(pasteTextOffset); + final pasteTextOffset = (_pastePosition.nodePosition as TextPosition).offset; + final attributionsAtPasteOffset = textNode.text.getAllAttributionsAt(pasteTextOffset); - if (splitContent.length > 1 && - pasteTextOffset < textNode.endPosition.offset) { + if (splitContent.length > 1 && pasteTextOffset < textNode.endPosition.offset) { // There is more than 1 node of content being pasted. Therefore, // new nodes will need to be added, which means that the currently // selected text node will be split at the current text offset. @@ -155,8 +145,7 @@ class _PasteEditorCommand implements EditorCommand { replicateExistingMetdata: false, ).execute(document, transaction); } else { - throw Exception( - 'Can\'t handle pasting text within node of type: $currentNodeWithSelection'); + throw Exception('Can\'t handle pasting text within node of type: $currentNodeWithSelection'); } } @@ -230,8 +219,7 @@ ExecutionInstruction copyWhenCmdVIsPressed({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.character?.toLowerCase() != 'c') { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'c') { return ExecutionInstruction.continueExecution; } if (editContext.composer.selection == null) { @@ -268,15 +256,13 @@ Future _copy({ if (i == 0) { // This is the first node and it may be partially selected. - final baseSelectionPosition = - selectedNode.id == documentSelection.base.nodeId - ? documentSelection.base.nodePosition - : documentSelection.extent.nodePosition; - - final extentSelectionPosition = selectedNodes.length > 1 - ? selectedNode.endPosition + final baseSelectionPosition = selectedNode.id == documentSelection.base.nodeId + ? documentSelection.base.nodePosition : documentSelection.extent.nodePosition; + final extentSelectionPosition = + selectedNodes.length > 1 ? selectedNode.endPosition : documentSelection.extent.nodePosition; + nodeSelection = selectedNode.computeSelection( base: baseSelectionPosition, extent: extentSelectionPosition, @@ -319,8 +305,7 @@ ExecutionInstruction cmdBToToggleBold({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.character?.toLowerCase() != 'b') { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'b') { return ExecutionInstruction.continueExecution; } @@ -337,8 +322,7 @@ ExecutionInstruction cmdIToToggleItalics({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.character?.toLowerCase() != 'i') { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.character?.toLowerCase() != 'i') { return ExecutionInstruction.continueExecution; } @@ -355,10 +339,8 @@ ExecutionInstruction anyCharacterOrDestructiveKeyToDeleteSelection({ required EditContext editContext, required RawKeyEvent keyEvent, }) { - _log.log('deleteExpandedSelectionWhenCharacterOrDestructiveKeyPressed', - 'Running...'); - if (editContext.composer.selection == null || - editContext.composer.selection!.isCollapsed) { + _log.log('deleteExpandedSelectionWhenCharacterOrDestructiveKeyPressed', 'Running...'); + if (editContext.composer.selection == null || editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } @@ -369,8 +351,7 @@ ExecutionInstruction anyCharacterOrDestructiveKeyToDeleteSelection({ final isShiftPressed = keyEvent.isShiftPressed; final isDestructiveKey = - keyEvent.logicalKey == LogicalKeyboardKey.backspace || - keyEvent.logicalKey == LogicalKeyboardKey.delete; + keyEvent.logicalKey == LogicalKeyboardKey.backspace || keyEvent.logicalKey == LogicalKeyboardKey.delete; final shouldDeleteSelection = !isShiftPressed && (isDestructiveKey || @@ -397,16 +378,14 @@ DocumentPosition _getDocumentPositionAfterDeletion({ final basePosition = selection.base; final baseNode = document.getNode(basePosition); if (baseNode == null) { - throw Exception( - 'Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); + throw Exception('Failed to _getDocumentPositionAfterDeletion because the base node no longer exists.'); } final baseNodeIndex = document.getNodeIndex(baseNode); final extentPosition = selection.extent; final extentNode = document.getNode(extentPosition); if (extentNode == null) { - throw Exception( - 'Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); + throw Exception('Failed to _getDocumentPositionAfterDeletion because the extent node no longer exists.'); } final extentNodeIndex = document.getNodeIndex(extentNode); DocumentPosition newSelectionPosition; @@ -414,8 +393,7 @@ DocumentPosition _getDocumentPositionAfterDeletion({ if (baseNodeIndex != extentNodeIndex) { // Place the caret at the current position within the // first node in the selection. - newSelectionPosition = - baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; + newSelectionPosition = baseNodeIndex <= extentNodeIndex ? selection.base : selection.extent; // If it's a binary selection node then that node will // be replaced by a ParagraphNode with the same ID. @@ -465,9 +443,7 @@ ExecutionInstruction backspaceToRemoveUpstreamContent({ final didDelete = editContext.commonOps.deleteUpstream(); - return didDelete - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction mergeNodeWithNextWhenDeleteIsPressed({ @@ -482,23 +458,19 @@ ExecutionInstruction mergeNodeWithNextWhenDeleteIsPressed({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document - .getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! TextNode) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', - 'WARNING: Cannot combine node of type: $node'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'WARNING: Cannot combine node of type: $node'); return ExecutionInstruction.continueExecution; } final nextNode = editContext.editor.document.getNodeAfter(node); if (nextNode == null) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', - 'At bottom of document. Cannot merge with node above.'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'At bottom of document. Cannot merge with node above.'); return ExecutionInstruction.continueExecution; } if (nextNode is! TextNode) { - _log.log('mergeNodeWithNextWhenDeleteIsPressed', - 'Cannot merge ParagraphNode into node of type: $nextNode'); + _log.log('mergeNodeWithNextWhenDeleteIsPressed', 'Cannot merge ParagraphNode into node of type: $nextNode'); return ExecutionInstruction.continueExecution; } @@ -539,10 +511,8 @@ ExecutionInstruction moveUpDownLeftAndRightWithArrowKeys({ } bool didMove = false; - if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft || - keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); + if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft || keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); final movementModifiers = {}; if (keyEvent.isPrimaryShortcutKeyPressed) { @@ -567,17 +537,12 @@ ExecutionInstruction moveUpDownLeftAndRightWithArrowKeys({ } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowUp) { _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); - didMove = - editContext.commonOps.moveCaretUp(expand: keyEvent.isShiftPressed); + didMove = editContext.commonOps.moveCaretUp(expand: keyEvent.isShiftPressed); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowDown) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); - didMove = - editContext.commonOps.moveCaretDown(expand: keyEvent.isShiftPressed); + didMove = editContext.commonOps.moveCaretDown(expand: keyEvent.isShiftPressed); } - return didMove - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didMove ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } diff --git a/super_editor/lib/src/default_editor/horizontal_rule.dart b/super_editor/lib/src/default_editor/horizontal_rule.dart index 51d3e044fa..f82a298e3c 100644 --- a/super_editor/lib/src/default_editor/horizontal_rule.dart +++ b/super_editor/lib/src/default_editor/horizontal_rule.dart @@ -18,22 +18,18 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { final String id; @override - BinaryNodePosition get beginningPosition => - const BinaryNodePosition.included(); + BinaryNodePosition get beginningPosition => const BinaryNodePosition.included(); @override BinaryNodePosition get endPosition => const BinaryNodePosition.included(); @override - NodePosition selectUpstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -42,15 +38,12 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { } @override - NodePosition selectDownstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -69,13 +62,10 @@ class HorizontalRuleNode with ChangeNotifier implements DocumentNode { @override String? copyContent(dynamic selection) { if (selection is! BinarySelection) { - throw Exception( - 'HorizontalRuleNode can only copy content from a BinarySelection.'); + throw Exception('HorizontalRuleNode can only copy content from a BinarySelection.'); } - return selection.position == const BinaryNodePosition.included() - ? '---' - : null; + return selection.position == const BinaryNodePosition.included() ? '---' : null; } @override @@ -128,16 +118,13 @@ Widget? horizontalRuleBuilder(ComponentContext componentContext) { return null; } - final selection = componentContext.nodeSelection == null - ? null - : componentContext.nodeSelection!.nodeSelection as BinarySelection; + final selection = + componentContext.nodeSelection == null ? null : componentContext.nodeSelection!.nodeSelection as BinarySelection; final isSelected = selection != null && selection.position.isIncluded; return HorizontalRuleComponent( componentKey: componentContext.componentKey, isSelected: isSelected, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, ); } diff --git a/super_editor/lib/src/default_editor/image.dart b/super_editor/lib/src/default_editor/image.dart index f84afcc0bc..4efb4907ca 100644 --- a/super_editor/lib/src/default_editor/image.dart +++ b/super_editor/lib/src/default_editor/image.dart @@ -38,22 +38,18 @@ class ImageNode with ChangeNotifier implements DocumentNode { } @override - BinaryNodePosition get beginningPosition => - const BinaryNodePosition.included(); + BinaryNodePosition get beginningPosition => const BinaryNodePosition.included(); @override BinaryNodePosition get endPosition => const BinaryNodePosition.included(); @override - NodePosition selectUpstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -62,15 +58,12 @@ class ImageNode with ChangeNotifier implements DocumentNode { } @override - NodePosition selectDownstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! BinaryNodePosition) { - throw Exception( - 'Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a BinaryNodePosition for position2 but received a ${position2.runtimeType}'); } // BinaryNodePosition's don't disambiguate between upstream and downstream so @@ -89,20 +82,15 @@ class ImageNode with ChangeNotifier implements DocumentNode { @override String? copyContent(dynamic selection) { if (selection is! BinarySelection) { - throw Exception( - 'ImageNode can only copy content from a BinarySelection.'); + throw Exception('ImageNode can only copy content from a BinarySelection.'); } - return selection.position == const BinaryNodePosition.included() - ? _imageUrl - : null; + return selection.position == const BinaryNodePosition.included() ? _imageUrl : null; } @override bool hasEquivalentContent(DocumentNode other) { - return other is ImageNode && - imageUrl == other.imageUrl && - altText == other.altText; + return other is ImageNode && imageUrl == other.imageUrl && altText == other.altText; } } @@ -150,17 +138,14 @@ Widget? imageBuilder(ComponentContext componentContext) { return null; } - final selection = componentContext.nodeSelection == null - ? null - : componentContext.nodeSelection!.nodeSelection as BinarySelection; + final selection = + componentContext.nodeSelection == null ? null : componentContext.nodeSelection!.nodeSelection as BinarySelection; final isSelected = selection != null && selection.position.isIncluded; return ImageComponent( componentKey: componentContext.componentKey, imageUrl: (componentContext.documentNode as ImageNode).imageUrl, isSelected: isSelected, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, ); } diff --git a/super_editor/lib/src/default_editor/list_items.dart b/super_editor/lib/src/default_editor/list_items.dart index 92e259b129..29e4c248f8 100644 --- a/super_editor/lib/src/default_editor/list_items.dart +++ b/super_editor/lib/src/default_editor/list_items.dart @@ -72,10 +72,7 @@ class ListItemNode extends TextNode { @override bool hasEquivalentContent(DocumentNode other) { - return other is ListItemNode && - type == other.type && - indent == other.indent && - text == other.text; + return other is ListItemNode && type == other.type && indent == other.indent && text == other.text; } } @@ -127,9 +124,7 @@ class UnorderedListItemComponent extends StatelessWidget { Container( margin: const EdgeInsets.only(top: manualVerticalAdjustment), decoration: BoxDecoration( - border: Border.all( - width: 1, - color: showDebugPaint ? Colors.grey : Colors.transparent), + border: Border.all(width: 1, color: showDebugPaint ? Colors.grey : Colors.transparent), ), child: SizedBox( width: indentSpace, @@ -154,11 +149,9 @@ class UnorderedListItemComponent extends StatelessWidget { } } -typedef UnorderedListItemDotBuilder = Widget Function( - BuildContext, UnorderedListItemComponent); +typedef UnorderedListItemDotBuilder = Widget Function(BuildContext, UnorderedListItemComponent); -Widget _defaultUnorderedListItemDotBuilder( - BuildContext context, UnorderedListItemComponent component) { +Widget _defaultUnorderedListItemDotBuilder(BuildContext context, UnorderedListItemComponent component) { return Align( alignment: Alignment.centerRight, child: Container( @@ -221,9 +214,7 @@ class OrderedListItemComponent extends StatelessWidget { height: firstLineHeight + manualHeightAdjustment, margin: const EdgeInsets.only(top: manualVerticalAdjustment), decoration: BoxDecoration( - border: Border.all( - width: 1, - color: showDebugPaint ? Colors.grey : Colors.transparent), + border: Border.all(width: 1, color: showDebugPaint ? Colors.grey : Colors.transparent), ), child: SizedBox( width: indentSpace, @@ -248,11 +239,9 @@ class OrderedListItemComponent extends StatelessWidget { } } -typedef OrderedListItemNumeralBuilder = Widget Function( - BuildContext, OrderedListItemComponent); +typedef OrderedListItemNumeralBuilder = Widget Function(BuildContext, OrderedListItemComponent); -Widget _defaultOrderedListItemNumeralBuilder( - BuildContext context, OrderedListItemComponent component) { +Widget _defaultOrderedListItemNumeralBuilder(BuildContext context, OrderedListItemComponent component) { return OverflowBox( maxHeight: double.infinity, child: Align( @@ -282,8 +271,7 @@ class IndentListItemCommand implements EditorCommand { final node = document.getNodeById(nodeId); final listItem = node as ListItemNode; if (listItem.indent >= 6) { - _log.log('IndentListItemCommand', - 'WARNING: Editor does not support an indent level beyond 6.'); + _log.log('IndentListItemCommand', 'WARNING: Editor does not support an indent level beyond 6.'); return; } @@ -408,16 +396,13 @@ class SplitListItemCommand implements EditorCommand { final listItemNode = node as ListItemNode; final text = listItemNode.text; final startText = text.copyText(0, splitPosition.offset); - final endText = splitPosition.offset < text.text.length - ? text.copyText(splitPosition.offset) - : AttributedText(); + final endText = splitPosition.offset < text.text.length ? text.copyText(splitPosition.offset) : AttributedText(); _log.log('SplitListItemCommand', 'Splitting list item:'); _log.log('SplitListItemCommand', ' - start text: "$startText"'); _log.log('SplitListItemCommand', ' - end text: "$endText"'); // Change the current node's content to just the text before the caret. - _log.log('SplitListItemCommand', - ' - changing the original list item text due to split'); + _log.log('SplitListItemCommand', ' - changing the original list item text due to split'); // TODO: figure out how node changes should work in terms of // a DocumentEditorTransaction (#67) listItemNode.text = startText; @@ -443,8 +428,7 @@ class SplitListItemCommand implements EditorCommand { newNode: newNode, ); - _log.log('SplitListItemCommand', - ' - inserted new node: ${newNode.id} after old one: ${node.id}'); + _log.log('SplitListItemCommand', ' - inserted new node: ${newNode.id} after old one: ${node.id}'); } } @@ -461,9 +445,7 @@ ExecutionInstruction tabToIndentListItem({ final wasIndented = editContext.commonOps.indentListItem(); - return wasIndented - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction shiftTabToUnIndentListItem({ @@ -479,9 +461,7 @@ ExecutionInstruction shiftTabToUnIndentListItem({ final wasIndented = editContext.commonOps.unindentListItem(); - return wasIndented - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction backspaceToUnIndentListItem({ @@ -499,22 +479,17 @@ ExecutionInstruction backspaceToUnIndentListItem({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document - .getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! ListItemNode) { return ExecutionInstruction.continueExecution; } - if ((editContext.composer.selection!.extent.nodePosition as TextPosition) - .offset > - 0) { + if ((editContext.composer.selection!.extent.nodePosition as TextPosition).offset > 0) { return ExecutionInstruction.continueExecution; } final wasIndented = editContext.commonOps.unindentListItem(); - return wasIndented - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return wasIndented ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction splitListItemWhenEnterPressed({ @@ -525,16 +500,13 @@ ExecutionInstruction splitListItemWhenEnterPressed({ return ExecutionInstruction.continueExecution; } - final node = editContext.editor.document - .getNodeById(editContext.composer.selection!.extent.nodeId); + final node = editContext.editor.document.getNodeById(editContext.composer.selection!.extent.nodeId); if (node is! ListItemNode) { return ExecutionInstruction.continueExecution; } final didSplitListItem = editContext.commonOps.insertBlockLevelNewline(); - return didSplitListItem - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didSplitListItem ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } Widget? unorderedListItemBuilder(ComponentContext componentContext) { @@ -547,10 +519,8 @@ Widget? unorderedListItemBuilder(ComponentContext componentContext) { return null; } - final textSelection = - componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && - (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); return UnorderedListItemComponent( textKey: componentContext.componentKey, @@ -558,13 +528,9 @@ Widget? unorderedListItemBuilder(ComponentContext componentContext) { styleBuilder: componentContext.extensions[textStylesExtensionKey], indent: listItemNode.indent, textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, ); } @@ -579,8 +545,7 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { } int index = 1; - DocumentNode? nodeAbove = - componentContext.document.getNodeBefore(listItemNode); + DocumentNode? nodeAbove = componentContext.document.getNodeBefore(listItemNode); while (nodeAbove != null && nodeAbove is ListItemNode && nodeAbove.type == ListItemType.ordered && @@ -591,10 +556,8 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { nodeAbove = componentContext.document.getNodeBefore(nodeAbove); } - final textSelection = - componentContext.nodeSelection?.nodeSelection as TextSelection?; - final showCaret = componentContext.showCaret && - (componentContext.nodeSelection?.isExtent ?? false); + final textSelection = componentContext.nodeSelection?.nodeSelection as TextSelection?; + final showCaret = componentContext.showCaret && (componentContext.nodeSelection?.isExtent ?? false); return OrderedListItemComponent( textKey: componentContext.componentKey, @@ -602,13 +565,9 @@ Widget? orderedListItemBuilder(ComponentContext componentContext) { text: listItemNode.text, styleBuilder: componentContext.extensions[textStylesExtensionKey], textSelection: textSelection, - selectionColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .selectionColor, + selectionColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).selectionColor, showCaret: showCaret, - caretColor: (componentContext.extensions[selectionStylesExtensionKey] - as SelectionStyle) - .textCaretColor, + caretColor: (componentContext.extensions[selectionStylesExtensionKey] as SelectionStyle).textCaretColor, indent: listItemNode.indent, ); } diff --git a/super_editor/lib/src/default_editor/super_editor.dart b/super_editor/lib/src/default_editor/super_editor.dart index 4836c7cc51..b9d1740397 100644 --- a/super_editor/lib/src/default_editor/super_editor.dart +++ b/super_editor/lib/src/default_editor/super_editor.dart @@ -263,14 +263,12 @@ class _SuperEditorState extends State { return; } - final node = - widget.editor.document.getNodeById(_composer.selection!.extent.nodeId); + final node = widget.editor.document.getNodeById(_composer.selection!.extent.nodeId); if (node is! TextNode) { return; } - final textPosition = - _composer.selection!.extent.nodePosition as TextPosition; + final textPosition = _composer.selection!.extent.nodePosition as TextPosition; if (textPosition.offset == 0) { if (node.text.text.isEmpty) { @@ -300,8 +298,7 @@ class _SuperEditorState extends State { commonOps: CommonEditorOperations( editor: widget.editor, composer: _composer, - documentLayoutResolver: () => - _docLayoutKey.currentState as DocumentLayout, + documentLayoutResolver: () => _docLayoutKey.currentState as DocumentLayout, ), ), keyboardActions: widget.keyboardActions, diff --git a/super_editor/lib/src/default_editor/text.dart b/super_editor/lib/src/default_editor/text.dart index 3c06e26091..bd7ed4b1eb 100644 --- a/super_editor/lib/src/default_editor/text.dart +++ b/super_editor/lib/src/default_editor/text.dart @@ -62,34 +62,27 @@ class TextNode with ChangeNotifier implements DocumentNode { TextNodePosition get beginningPosition => const TextNodePosition(offset: 0); @override - TextNodePosition get endPosition => - TextNodePosition(offset: text.text.length); + TextNodePosition get endPosition => TextNodePosition(offset: text.text.length); @override - NodePosition selectUpstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectUpstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! TextNodePosition) { - throw Exception( - 'Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! TextNodePosition) { - throw Exception( - 'Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); } return position1.offset < position2.offset ? position1 : position2; } @override - NodePosition selectDownstreamPosition( - NodePosition position1, NodePosition position2) { + NodePosition selectDownstreamPosition(NodePosition position1, NodePosition position2) { if (position1 is! TextNodePosition) { - throw Exception( - 'Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); + throw Exception('Expected a TextNodePosition for position1 but received a ${position1.runtimeType}'); } if (position2 is! TextNodePosition) { - throw Exception( - 'Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); + throw Exception('Expected a TextNodePosition for position2 but received a ${position2.runtimeType}'); } return position1.offset > position2.offset ? position1 : position2; @@ -118,9 +111,7 @@ class TextNode with ChangeNotifier implements DocumentNode { @override bool hasEquivalentContent(DocumentNode other) { - return other is TextNode && - text == other.text && - const DeepCollectionEquality().equals(metadata, other.metadata); + return other is TextNode && text == other.text && const DeepCollectionEquality().equals(metadata, other.metadata); } @override @@ -211,9 +202,7 @@ class TextComponent extends StatefulWidget { _TextComponentState createState() => _TextComponentState(); } -class _TextComponentState extends State - with DocumentComponent - implements TextComposable { +class _TextComponentState extends State with DocumentComponent implements TextComposable { final _selectableTextKey = GlobalKey(); @override @@ -229,8 +218,7 @@ class _TextComponentState extends State @override Offset getOffsetForPosition(dynamic nodePosition) { if (nodePosition is! TextPosition) { - throw Exception( - 'Expected nodePosition of type TextPosition but received: $nodePosition'); + throw Exception('Expected nodePosition of type TextPosition but received: $nodePosition'); } return _selectableTextKey.currentState!.getOffsetAtPosition(nodePosition); } @@ -238,8 +226,7 @@ class _TextComponentState extends State @override Rect getRectForPosition(dynamic nodePosition) { if (nodePosition is! TextPosition) { - throw Exception( - 'Expected nodePosition of type TextPosition but received: $nodePosition'); + throw Exception('Expected nodePosition of type TextPosition but received: $nodePosition'); } // TODO: factor in line height for position rect @@ -248,22 +235,16 @@ class _TextComponentState extends State } @override - Rect getRectForSelection( - dynamic baseNodePosition, dynamic extentNodePosition) { + Rect getRectForSelection(dynamic baseNodePosition, dynamic extentNodePosition) { if (baseNodePosition is! TextPosition) { - throw Exception( - 'Expected nodePosition of type TextPosition but received: $baseNodePosition'); + throw Exception('Expected nodePosition of type TextPosition but received: $baseNodePosition'); } if (extentNodePosition is! TextPosition) { - throw Exception( - 'Expected nodePosition of type TextPosition but received: $extentNodePosition'); + throw Exception('Expected nodePosition of type TextPosition but received: $extentNodePosition'); } - final selection = TextSelection( - baseOffset: baseNodePosition.offset, - extentOffset: extentNodePosition.offset); - final boxes = - _selectableTextKey.currentState!.getBoxesForSelection(selection); + final selection = TextSelection(baseOffset: baseNodePosition.offset, extentOffset: extentNodePosition.offset); + final boxes = _selectableTextKey.currentState!.getBoxesForSelection(selection); Rect boundingBox = boxes.isNotEmpty ? boxes.first.toRect() : Rect.zero; for (int i = 1; i < boxes.length; ++i) { @@ -286,8 +267,7 @@ class _TextComponentState extends State } @override - TextNodePosition? movePositionLeft(NodePosition textPosition, - [Set? movementModifiers]) { + TextNodePosition? movePositionLeft(NodePosition textPosition, [Set? movementModifiers]) { if (textPosition is! TextNodePosition) { // We don't know how to interpret a non-text position. return null; @@ -303,13 +283,11 @@ class _TextComponentState extends State return null; } - if (movementModifiers != null && - movementModifiers.contains(MovementModifier.line)) { + if (movementModifiers != null && movementModifiers.contains(MovementModifier.line)) { return getPositionAtStartOfLine( TextNodePosition(offset: textPosition.offset), ); - } else if (movementModifiers != null && - movementModifiers.contains(MovementModifier.word)) { + } else if (movementModifiers != null && movementModifiers.contains(MovementModifier.word)) { final text = getContiguousTextAt(textPosition); int newOffset = textPosition.offset; @@ -324,8 +302,7 @@ class _TextComponentState extends State } @override - TextNodePosition? movePositionRight(NodePosition textPosition, - [Set? movementModifiers]) { + TextNodePosition? movePositionRight(NodePosition textPosition, [Set? movementModifiers]) { if (textPosition is! TextNodePosition) { // We don't know how to interpret a non-text position. return null; @@ -336,8 +313,7 @@ class _TextComponentState extends State return null; } - if (movementModifiers != null && - movementModifiers.contains(MovementModifier.line)) { + if (movementModifiers != null && movementModifiers.contains(MovementModifier.line)) { final endOfLine = getPositionAtEndOfLine( TextNodePosition(offset: textPosition.offset), ); @@ -346,8 +322,7 @@ class _TextComponentState extends State final text = getContiguousTextAt(endOfLine); // Note: we compare offset values because we don't care if the affinitys are equal - final isAutoWrapLine = endOfLine.offset != endPosition.offset && - (text[endOfLine.offset] != '\n'); + final isAutoWrapLine = endOfLine.offset != endPosition.offset && (text[endOfLine.offset] != '\n'); // Note: For lines that auto-wrap, moving the cursor to `offset` causes the // cursor to jump to the next line because the cursor is placed after @@ -365,8 +340,7 @@ class _TextComponentState extends State ? TextNodePosition(offset: endOfLine.offset - 1) : TextNodePosition.fromTextPosition(endOfLine); } - if (movementModifiers != null && - movementModifiers.contains(MovementModifier.word)) { + if (movementModifiers != null && movementModifiers.contains(MovementModifier.word)) { final text = getContiguousTextAt(textPosition); int newOffset = textPosition.offset; @@ -387,8 +361,7 @@ class _TextComponentState extends State return null; } - if (textNodePosition.offset < 0 || - textNodePosition.offset > widget.text.text.length) { + if (textNodePosition.offset < 0 || textNodePosition.offset > widget.text.text.length) { // This text position does not represent a position within our text. return null; } @@ -407,8 +380,7 @@ class _TextComponentState extends State return null; } - if (textNodePosition.offset < 0 || - textNodePosition.offset > widget.text.text.length) { + if (textNodePosition.offset < 0 || textNodePosition.offset > widget.text.text.length) { // This text position does not represent a position within our text. return null; } @@ -427,22 +399,19 @@ class _TextComponentState extends State @override TextNodePosition getEndPositionNearX(double x) { - return TextNodePosition.fromTextPosition( - _selectableTextKey.currentState!.getPositionInLastLineAtX(x)); + return TextNodePosition.fromTextPosition(_selectableTextKey.currentState!.getPositionInLastLineAtX(x)); } @override - TextNodeSelection getSelectionInRange( - Offset localBaseOffset, Offset localExtentOffset) { - return TextNodeSelection.fromTextSelection(_selectableTextKey.currentState! - .getSelectionInRect(localBaseOffset, localExtentOffset)); + TextNodeSelection getSelectionInRange(Offset localBaseOffset, Offset localExtentOffset) { + return TextNodeSelection.fromTextSelection( + _selectableTextKey.currentState!.getSelectionInRect(localBaseOffset, localExtentOffset)); } @override TextNodeSelection getCollapsedSelectionAt(NodePosition textNodePosition) { if (textNodePosition is! TextNodePosition) { - throw Exception( - 'The given node position ($textNodePosition) is not compatible with TextComponent'); + throw Exception('The given node position ($textNodePosition) is not compatible with TextComponent'); } return TextNodeSelection.collapsed(offset: textNodePosition.offset); @@ -454,12 +423,10 @@ class _TextComponentState extends State required NodePosition extentPosition, }) { if (basePosition is! TextNodePosition) { - throw Exception( - 'Expected a basePosition of type TextNodePosition but received: $basePosition'); + throw Exception('Expected a basePosition of type TextNodePosition but received: $basePosition'); } if (extentPosition is! TextNodePosition) { - throw Exception( - 'Expected an extentPosition of type TextNodePosition but received: $extentPosition'); + throw Exception('Expected an extentPosition of type TextNodePosition but received: $extentPosition'); } return TextNodeSelection( @@ -478,9 +445,7 @@ class _TextComponentState extends State @override MouseCursor? getDesiredCursorAtOffset(Offset localOffset) { - return _selectableTextKey.currentState!.isTextAtOffset(localOffset) - ? SystemMouseCursors.text - : null; + return _selectableTextKey.currentState!.isTextAtOffset(localOffset) ? SystemMouseCursors.text : null; } @override @@ -504,12 +469,10 @@ class _TextComponentState extends State @override TextNodePosition? getPositionOneLineUp(NodePosition textPosition) { if (textPosition is! TextNodePosition) { - throw Exception( - 'Expected position of type NodePosition but received ${textPosition.runtimeType}'); + throw Exception('Expected position of type NodePosition but received ${textPosition.runtimeType}'); } - final positionOneLineUp = - _selectableTextKey.currentState!.getPositionOneLineUp(textPosition); + final positionOneLineUp = _selectableTextKey.currentState!.getPositionOneLineUp(textPosition); if (positionOneLineUp == null) { return null; } @@ -519,12 +482,10 @@ class _TextComponentState extends State @override TextNodePosition? getPositionOneLineDown(NodePosition textPosition) { if (textPosition is! TextNodePosition) { - throw Exception( - 'Expected position of type NodePosition but received ${textPosition.runtimeType}'); + throw Exception('Expected position of type NodePosition but received ${textPosition.runtimeType}'); } - final positionOneLineDown = - _selectableTextKey.currentState!.getPositionOneLineDown(textPosition); + final positionOneLineDown = _selectableTextKey.currentState!.getPositionOneLineDown(textPosition); if (positionOneLineDown == null) { return null; } @@ -541,8 +502,7 @@ class _TextComponentState extends State @override TextNodePosition getPositionAtStartOfLine(TextNodePosition textNodePosition) { return TextNodePosition.fromTextPosition( - _selectableTextKey.currentState! - .getPositionAtStartOfLine(textNodePosition), + _selectableTextKey.currentState!.getPositionAtStartOfLine(textNodePosition), ); } @@ -566,10 +526,8 @@ class _TextComponentState extends State key: _selectableTextKey, textSpan: richText, textAlign: widget.textAlign ?? TextAlign.left, - textSelection: - widget.textSelection ?? const TextSelection.collapsed(offset: -1), - textSelectionDecoration: - TextSelectionDecoration(selectionColor: widget.selectionColor), + textSelection: widget.textSelection ?? const TextSelection.collapsed(offset: -1), + textSelectionDecoration: TextSelectionDecoration(selectionColor: widget.selectionColor), showCaret: widget.showCaret, textCaretFactory: TextCaretFactory(color: widget.caretColor), highlightWhenEmpty: widget.highlightWhenEmpty, @@ -591,10 +549,8 @@ class AddTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log( - 'AddTextAttributionsCommand', 'Executing AddTextAttributionsCommand'); - final nodes = document.getNodesInside( - documentSelection.base, documentSelection.extent); + _log.log('AddTextAttributionsCommand', 'Executing AddTextAttributionsCommand'); + final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('AddTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -603,8 +559,7 @@ class AddTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween( - documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); _log.log('AddTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -620,12 +575,9 @@ class AddTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('AddTextAttributionsCommand', - ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = - (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = - (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('AddTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -634,14 +586,12 @@ class AddTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('AddTextAttributionsCommand', - ' - selecting part of the first node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('AddTextAttributionsCommand', - ' - adding part of the last node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', ' - adding part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -649,8 +599,7 @@ class AddTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('AddTextAttributionsCommand', - ' - adding full node: ${textNode.id}'); + _log.log('AddTextAttributionsCommand', ' - adding full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -665,8 +614,7 @@ class AddTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('AddTextAttributionsCommand', - ' - adding attribution: $attribution. Range: $range'); + _log.log('AddTextAttributionsCommand', ' - adding attribution: $attribution. Range: $range'); node.text.addAttribution( attribution, range, @@ -690,10 +638,8 @@ class RemoveTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log('RemoveTextAttributionsCommand', - 'Executing RemoveTextAttributionsCommand'); - final nodes = document.getNodesInside( - documentSelection.base, documentSelection.extent); + _log.log('RemoveTextAttributionsCommand', 'Executing RemoveTextAttributionsCommand'); + final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('RemoveTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -702,8 +648,7 @@ class RemoveTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween( - documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); _log.log('RemoveTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -719,12 +664,9 @@ class RemoveTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('RemoveTextAttributionsCommand', - ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = - (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = - (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('RemoveTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -733,14 +675,12 @@ class RemoveTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('RemoveTextAttributionsCommand', - ' - selecting part of the first node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('RemoveTextAttributionsCommand', - ' - adding part of the last node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', ' - adding part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -748,8 +688,7 @@ class RemoveTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('RemoveTextAttributionsCommand', - ' - adding full node: ${textNode.id}'); + _log.log('RemoveTextAttributionsCommand', ' - adding full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -764,8 +703,7 @@ class RemoveTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('RemoveTextAttributionsCommand', - ' - removing attribution: $attribution. Range: $range'); + _log.log('RemoveTextAttributionsCommand', ' - removing attribution: $attribution. Range: $range'); node.text.removeAttribution( attribution, range, @@ -792,10 +730,8 @@ class ToggleTextAttributionsCommand implements EditorCommand { @override void execute(Document document, DocumentEditorTransaction transaction) { - _log.log('ToggleTextAttributionsCommand', - 'Executing ToggleTextAttributionsCommand'); - final nodes = document.getNodesInside( - documentSelection.base, documentSelection.extent); + _log.log('ToggleTextAttributionsCommand', 'Executing ToggleTextAttributionsCommand'); + final nodes = document.getNodesInside(documentSelection.base, documentSelection.extent); if (nodes.isEmpty) { _log.log('ToggleTextAttributionsCommand', ' - Bad DocumentSelection. Could not get range of nodes. Selection: $documentSelection'); @@ -804,8 +740,7 @@ class ToggleTextAttributionsCommand implements EditorCommand { // Calculate a DocumentRange so we know which DocumentPosition // belongs to the first node, and which belongs to the last node. - final nodeRange = document.getRangeBetween( - documentSelection.base, documentSelection.extent); + final nodeRange = document.getRangeBetween(documentSelection.base, documentSelection.extent); _log.log('ToggleTextAttributionsCommand', ' - node range: $nodeRange'); // ignore: prefer_collection_literals @@ -822,12 +757,9 @@ class ToggleTextAttributionsCommand implements EditorCommand { if (textNode == nodes.first && textNode == nodes.last) { // Handle selection within a single node - _log.log('ToggleTextAttributionsCommand', - ' - the selection is within a single node: ${textNode.id}'); - final baseOffset = - (documentSelection.base.nodePosition as TextPosition).offset; - final extentOffset = - (documentSelection.extent.nodePosition as TextPosition).offset; + _log.log('ToggleTextAttributionsCommand', ' - the selection is within a single node: ${textNode.id}'); + final baseOffset = (documentSelection.base.nodePosition as TextPosition).offset; + final extentOffset = (documentSelection.extent.nodePosition as TextPosition).offset; startOffset = baseOffset < extentOffset ? baseOffset : extentOffset; endOffset = baseOffset < extentOffset ? extentOffset : baseOffset; @@ -836,14 +768,12 @@ class ToggleTextAttributionsCommand implements EditorCommand { endOffset -= 1; } else if (textNode == nodes.first) { // Handle partial node selection in first node. - _log.log('ToggleTextAttributionsCommand', - ' - selecting part of the first node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', ' - selecting part of the first node: ${textNode.id}'); startOffset = (nodeRange.start.nodePosition as TextPosition).offset; endOffset = max(textNode.text.text.length - 1, 0); } else if (textNode == nodes.last) { // Handle partial node selection in last node. - _log.log('ToggleTextAttributionsCommand', - ' - toggling part of the last node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', ' - toggling part of the last node: ${textNode.id}'); startOffset = 0; // -1 because TextPosition's offset indexes the character after the @@ -851,8 +781,7 @@ class ToggleTextAttributionsCommand implements EditorCommand { endOffset = (nodeRange.end.nodePosition as TextPosition).offset - 1; } else { // Handle full node selection. - _log.log('ToggleTextAttributionsCommand', - ' - toggling full node: ${textNode.id}'); + _log.log('ToggleTextAttributionsCommand', ' - toggling full node: ${textNode.id}'); startOffset = 0; endOffset = max(textNode.text.text.length - 1, 0); } @@ -873,8 +802,7 @@ class ToggleTextAttributionsCommand implements EditorCommand { for (Attribution attribution in attributions) { final node = entry.key; final range = entry.value; - _log.log('ToggleTextAttributionsCommand', - ' - toggling attribution: $attribution. Range: $range'); + _log.log('ToggleTextAttributionsCommand', ' - toggling attribution: $attribution. Range: $range'); node.text.toggleAttribution( attribution, range, @@ -901,8 +829,7 @@ class InsertTextCommand implements EditorCommand { void execute(Document document, DocumentEditorTransaction transaction) { final textNode = document.getNodeById(documentPosition.nodeId); if (textNode is! TextNode) { - _log.log('InsertTextCommand', - 'ERROR: can\'t insert text in a node that isn\'t a TextNode: $textNode'); + _log.log('InsertTextCommand', 'ERROR: can\'t insert text in a node that isn\'t a TextNode: $textNode'); return; } @@ -929,9 +856,7 @@ ExecutionInstruction anyCharacterToInsertInTextContent({ if (!editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } - if (!_isTextEntryNode( - document: editContext.editor.document, - selection: editContext.composer.selection!)) { + if (!_isTextEntryNode(document: editContext.editor.document, selection: editContext.composer.selection!)) { return ExecutionInstruction.continueExecution; } if (keyEvent.character == null || keyEvent.character == '') { @@ -959,9 +884,7 @@ ExecutionInstruction anyCharacterToInsertInTextContent({ final didInsertCharacter = editContext.commonOps.insertCharacter(character); - return didInsertCharacter - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didInsertCharacter ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction deleteCharacterWhenBackspaceIsPressed({ @@ -974,25 +897,19 @@ ExecutionInstruction deleteCharacterWhenBackspaceIsPressed({ if (editContext.composer.selection == null) { return ExecutionInstruction.continueExecution; } - if (!_isTextEntryNode( - document: editContext.editor.document, - selection: editContext.composer.selection!)) { + if (!_isTextEntryNode(document: editContext.editor.document, selection: editContext.composer.selection!)) { return ExecutionInstruction.continueExecution; } if (!editContext.composer.selection!.isCollapsed) { return ExecutionInstruction.continueExecution; } - if ((editContext.composer.selection!.extent.nodePosition as TextPosition) - .offset <= - 0) { + if ((editContext.composer.selection!.extent.nodePosition as TextPosition).offset <= 0) { return ExecutionInstruction.continueExecution; } final didDelete = editContext.commonOps.deleteUpstream(); - return didDelete - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction deleteToRemoveDownstreamContent({ @@ -1005,9 +922,7 @@ ExecutionInstruction deleteToRemoveDownstreamContent({ final didDelete = editContext.commonOps.deleteDownstream(); - return didDelete - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didDelete ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } ExecutionInstruction shiftEnterToInsertNewlineInBlock({ @@ -1023,9 +938,7 @@ ExecutionInstruction shiftEnterToInsertNewlineInBlock({ final didInsertNewline = editContext.commonOps.insertPlainText('\n'); - return didInsertNewline - ? ExecutionInstruction.haltExecution - : ExecutionInstruction.continueExecution; + return didInsertNewline ? ExecutionInstruction.haltExecution : ExecutionInstruction.continueExecution; } bool _isTextEntryNode({ diff --git a/super_editor/lib/src/default_editor/text_tools.dart b/super_editor/lib/src/default_editor/text_tools.dart index bbf53ebd94..5bb1bdd6ec 100644 --- a/super_editor/lib/src/default_editor/text_tools.dart +++ b/super_editor/lib/src/default_editor/text_tools.dart @@ -34,10 +34,8 @@ DocumentSelection? getWordSelection({ return null; } - final TextSelection wordTextSelection = - (component as TextComposable).getWordSelectionAt(nodePosition); - final wordNodeSelection = - TextNodeSelection.fromTextSelection(wordTextSelection); + final TextSelection wordTextSelection = (component as TextComposable).getWordSelectionAt(nodePosition); + final wordNodeSelection = TextNodeSelection.fromTextSelection(wordTextSelection); _log.log('getWordSelection', ' - word selection: $wordNodeSelection'); return DocumentSelection( @@ -102,8 +100,7 @@ DocumentSelection? getParagraphSelection({ text: (component as TextComposable).getContiguousTextAt(nodePosition), textPosition: docPosition.nodePosition as TextPosition, ); - final paragraphNodeSelection = - TextNodeSelection.fromTextSelection(paragraphTextSelection); + final paragraphNodeSelection = TextNodeSelection.fromTextSelection(paragraphTextSelection); return DocumentSelection( base: DocumentPosition( diff --git a/super_editor/lib/src/infrastructure/attributed_spans.dart b/super_editor/lib/src/infrastructure/attributed_spans.dart index 2c3e8f5e8d..981bceb0a2 100644 --- a/super_editor/lib/src/infrastructure/attributed_spans.dart +++ b/super_editor/lib/src/infrastructure/attributed_spans.dart @@ -107,16 +107,13 @@ class AttributedSpans { int offset, { Attribution? attribution, }) { - SpanMarker? markerBefore = - _getStartingMarkerAtOrBefore(offset, attribution: attribution); + SpanMarker? markerBefore = _getStartingMarkerAtOrBefore(offset, attribution: attribution); if (markerBefore == null) { return false; } - SpanMarker? markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, - attribution: attribution); + SpanMarker? markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, attribution: attribution); if (markerAfter == null) { - throw Exception( - 'Found an open-ended attribution. It starts with: $markerBefore'); + throw Exception('Found an open-ended attribution. It starts with: $markerBefore'); } return (markerBefore.offset <= offset) && (offset <= markerAfter.offset); @@ -134,10 +131,8 @@ class AttributedSpans { // The following methods should be guaranteed to produce non-null // values because we already verified that the given attribution // exists at the given offset. - SpanMarker markerBefore = - _getStartingMarkerAtOrBefore(offset, attribution: attribution)!; - SpanMarker markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, - attribution: attribution)!; + SpanMarker markerBefore = _getStartingMarkerAtOrBefore(offset, attribution: attribution)!; + SpanMarker markerAfter = _getEndingMarkerAtOrAfter(markerBefore.offset, attribution: attribution)!; return AttributionSpan( attribution: attribution, @@ -218,30 +213,25 @@ class AttributedSpans { /// Finds and returns the nearest [start] marker that appears at or before the /// given [offset], optionally looking specifically for a marker with /// the given [attribution]. - SpanMarker? _getStartingMarkerAtOrBefore(int offset, - {Attribution? attribution}) { + SpanMarker? _getStartingMarkerAtOrBefore(int offset, {Attribution? attribution}) { return _attributions // .reversed // search from the end so its the nearest start marker .where((marker) { return attribution == null || - (marker.attribution.id == attribution.id && - marker.attribution.canMergeWith(attribution)); + (marker.attribution.id == attribution.id && marker.attribution.canMergeWith(attribution)); }) // .where((marker) => attribution == null || marker.attribution.id == attribution.id) - .firstWhereOrNull( - (marker) => marker.isStart && marker.offset <= offset); + .firstWhereOrNull((marker) => marker.isStart && marker.offset <= offset); } /// Finds and returns the nearest [end] marker that appears at or after the /// given [offset], optionally looking specifically for a marker with /// the given [attribution]. - SpanMarker? _getEndingMarkerAtOrAfter(int offset, - {Attribution? attribution}) { + SpanMarker? _getEndingMarkerAtOrAfter(int offset, {Attribution? attribution}) { return _attributions .where((marker) => attribution == null || - (marker.attribution.id == attribution.id && - marker.attribution.canMergeWith(attribution))) + (marker.attribution.id == attribution.id && marker.attribution.canMergeWith(attribution))) .firstWhereOrNull((marker) => marker.isEnd && marker.offset >= offset); } @@ -264,8 +254,7 @@ class AttributedSpans { // Ensure that no conflicting attribution overlaps the new attribution. // If a conflict exists, throw an exception. - final matchingAttributions = getMatchingAttributionsWithin( - attributions: {newAttribution}, start: start, end: end); + final matchingAttributions = getMatchingAttributionsWithin(attributions: {newAttribution}, start: start, end: end); if (matchingAttributions.isNotEmpty) { for (final matchingAttribution in matchingAttributions) { if (!newAttribution.canMergeWith(matchingAttribution)) { @@ -303,15 +292,12 @@ class AttributedSpans { .where((attribution) => attribution.offset > start) .where((attribution) => attribution.offset <= end) .toList(); - _log.log('addAttribution', - 'removing ${markersToDelete.length} markers between $start and $end'); + _log.log('addAttribution', 'removing ${markersToDelete.length} markers between $start and $end'); _attributions.removeWhere((element) => markersToDelete.contains(element)); - final lastDeletedMarker = - markersToDelete.isNotEmpty ? markersToDelete.last : null; + final lastDeletedMarker = markersToDelete.isNotEmpty ? markersToDelete.last : null; - if (lastDeletedMarker == null || - lastDeletedMarker.markerType == SpanMarkerType.end) { + if (lastDeletedMarker == null || lastDeletedMarker.markerType == SpanMarkerType.end) { // If we didn't delete any markers, the span that began at // `range.start` or before needs to be capped off. // @@ -329,9 +315,7 @@ class AttributedSpans { // doesn't need to be inserted. _log.log('addAttribution', 'all attributions after:'); - _attributions - .where((element) => element.attribution == newAttribution) - .forEach((element) { + _attributions.where((element) => element.attribution == newAttribution).forEach((element) { _log.log('addAttribution', '$element'); }); } @@ -343,8 +327,7 @@ class AttributedSpans { required int end, }) { if (start < 0 || start > end) { - throw Exception( - 'removeAttribution() did not satisfy start < 0 and start > end, start: $start, end: $end'); + throw Exception('removeAttribution() did not satisfy start < 0 and start > end, start: $start, end: $end'); } // It's possible that a span we want to remove was started before the @@ -408,14 +391,11 @@ class AttributedSpans { .where((attribution) => attribution.offset >= start) .where((attribution) => attribution.offset <= end) .toList(); - _log.log('removeAttribution', - 'removing ${markersToDelete.length} markers between $start and $end'); + _log.log('removeAttribution', 'removing ${markersToDelete.length} markers between $start and $end'); _attributions.removeWhere((element) => markersToDelete.contains(element)); _log.log('removeAttribution', 'all attributions after:'); - _attributions - .where((element) => element.attribution == attributionToRemove) - .forEach((element) { + _attributions.where((element) => element.attribution == attributionToRemove).forEach((element) { _log.log('removeAttribution', ' - $element'); }); } @@ -429,10 +409,8 @@ class AttributedSpans { required int start, required int end, }) { - if (_isContinuousAttribution( - attribution: attribution, start: start, end: end)) { - removeAttribution( - attributionToRemove: attribution, start: start, end: end); + if (_isContinuousAttribution(attribution: attribution, start: start, end: end)) { + removeAttribution(attributionToRemove: attribution, start: start, end: end); } else { addAttribution(newAttribution: attribution, start: start, end: end); } @@ -446,10 +424,8 @@ class AttributedSpans { required int start, required int end, }) { - _log.log('_isContinuousAttribution', - 'attribution: "$attribution", range: $start -> $end'); - SpanMarker? markerBefore = - _getNearestMarkerAtOrBefore(start, attribution: attribution); + _log.log('_isContinuousAttribution', 'attribution: "$attribution", range: $start -> $end'); + SpanMarker? markerBefore = _getNearestMarkerAtOrBefore(start, attribution: attribution); _log.log('_isContinuousAttribution', 'marker before: $markerBefore'); if (markerBefore == null || markerBefore.isEnd) { @@ -458,19 +434,15 @@ class AttributedSpans { final indexBefore = _attributions.indexOf(markerBefore); final nextMarker = _attributions.sublist(indexBefore).firstWhereOrNull( - (marker) => - marker.attribution == attribution && - marker.offset > markerBefore.offset, + (marker) => marker.attribution == attribution && marker.offset > markerBefore.offset, ); _log.log('_isContinuousAttribution', 'next marker: $nextMarker'); if (nextMarker == null) { - throw Exception( - 'Inconsistent attributions state. Found a `start` marker with no matching `end`.'); + throw Exception('Inconsistent attributions state. Found a `start` marker with no matching `end`.'); } if (nextMarker.isStart) { - throw Exception( - 'Inconsistent attributions state. Found a `start` marker following a `start` marker.'); + throw Exception('Inconsistent attributions state. Found a `start` marker following a `start` marker.'); } // If there is even one additional marker in the `range` @@ -487,9 +459,8 @@ class AttributedSpans { Attribution? attribution, }) { SpanMarker? markerBefore; - final markers = attribution != null - ? _attributions.where((marker) => marker.attribution == attribution) - : _attributions; + final markers = + attribution != null ? _attributions.where((marker) => marker.attribution == attribution) : _attributions; for (final marker in markers) { if (marker.offset <= offset) { @@ -516,8 +487,8 @@ class AttributedSpans { /// Precondition: There must not already exist a marker with /// the same attribution at the same offset. void _insertMarker(SpanMarker newMarker) { - SpanMarker? markerAfter = _attributions.firstWhereOrNull( - (existingMarker) => existingMarker.offset >= newMarker.offset); + SpanMarker? markerAfter = + _attributions.firstWhereOrNull((existingMarker) => existingMarker.offset >= newMarker.offset); if (markerAfter != null) { final markerAfterIndex = _attributions.indexOf(markerAfter); @@ -555,8 +526,7 @@ class AttributedSpans { final pushedSpans = other.copy()..pushAttributionsBack(pushDistance); // Combine `this` and `other` attributions into one list. - final List combinedAttributions = List.from(_attributions) - ..addAll(pushedSpans._attributions); + final List combinedAttributions = List.from(_attributions)..addAll(pushedSpans._attributions); _log.log('addAt', 'combined attributions before merge:'); for (final marker in combinedAttributions) { _log.log('addAt', ' - $marker'); @@ -579,32 +549,25 @@ class AttributedSpans { /// Given a list of [attributions], which includes two different lists of /// attributions concatenated together at [mergePoint], merges any /// attribution spans that exist back-to-back at the [mergePoint]. - void _mergeBackToBackAttributions( - List attributions, int mergePoint) { - _log.log( - '_mergeBackToBackAttributions', 'merging attributions at $mergePoint'); + void _mergeBackToBackAttributions(List attributions, int mergePoint) { + _log.log('_mergeBackToBackAttributions', 'merging attributions at $mergePoint'); // Look for any compatible attributions at // `mergePoint - 1` and `mergePoint` and combine them. - final endAtMergePointMarkers = attributions - .where((marker) => marker.isEnd && marker.offset == mergePoint - 1) - .toList(); - final startAtMergePointMarkers = attributions - .where((marker) => marker.isStart && marker.offset == mergePoint) - .toList(); + final endAtMergePointMarkers = + attributions.where((marker) => marker.isEnd && marker.offset == mergePoint - 1).toList(); + final startAtMergePointMarkers = + attributions.where((marker) => marker.isStart && marker.offset == mergePoint).toList(); for (final startMarker in startAtMergePointMarkers) { - _log.log( - '_mergeBackToBackAttributions', 'marker on right side: $startMarker'); + _log.log('_mergeBackToBackAttributions', 'marker on right side: $startMarker'); final endMarker = endAtMergePointMarkers.firstWhereOrNull( (marker) => marker.attribution == startMarker.attribution, ); - _log.log('_mergeBackToBackAttributions', - 'matching marker on left side? $endMarker'); + _log.log('_mergeBackToBackAttributions', 'matching marker on left side? $endMarker'); if (endMarker != null) { // These two attributions should be combined into one. // To do this, delete these two markers from the original // attribution list. - _log.log('_mergeBackToBackAttributions', - 'combining left/right spans at edge at index $mergePoint'); + _log.log('_mergeBackToBackAttributions', 'combining left/right spans at edge at index $mergePoint'); _log.log('_mergeBackToBackAttributions', 'Removing markers:'); _log.log('_mergeBackToBackAttributions', ' - $startMarker'); _log.log('_mergeBackToBackAttributions', ' - $endMarker'); @@ -624,8 +587,7 @@ class AttributedSpans { final List cutAttributions = []; - _log.log('copyAttributionRegion', - 'inspecting existing markers in full AttributedSpans'); + _log.log('copyAttributionRegion', 'inspecting existing markers in full AttributedSpans'); final Map foundStartMarkers = {}; final Map foundEndMarkers = {}; @@ -635,22 +597,18 @@ class AttributedSpans { _attributions // .where((marker) => marker.offset < startOffset) // .forEach((marker) { - _log.log( - 'copyAttributionRegion', 'marker before the copy region: $marker'); + _log.log('copyAttributionRegion', 'marker before the copy region: $marker'); // Track any markers that begin before the `startOffset` // and continue beyond `startOffset`. if (marker.isStart) { - _log.log('copyAttributionRegion', - 'remembering this marker to insert in copied region'); + _log.log('copyAttributionRegion', 'remembering this marker to insert in copied region'); foundStartMarkers.putIfAbsent(marker.attribution, () => 0); - foundStartMarkers[marker.attribution] = - foundStartMarkers[marker.attribution]! + 1; + foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! + 1; } else { _log.log('copyAttributionRegion', 'this marker counters an earlier one we found. We will not re-insert this marker in the copied region'); foundStartMarkers.putIfAbsent(marker.attribution, () => 0); - foundStartMarkers[marker.attribution] = - foundStartMarkers[marker.attribution]! - 1; + foundStartMarkers[marker.attribution] = foundStartMarkers[marker.attribution]! - 1; } }); @@ -675,8 +633,7 @@ class AttributedSpans { // Directly copy every marker that appears within the cut // region. _attributions // - .where((marker) => - startOffset <= marker.offset && marker.offset <= endOffset!) // + .where((marker) => startOffset <= marker.offset && marker.offset <= endOffset!) // .forEach((marker) { _log.log('copyAttributionRegion', 'copying "${marker.attribution}" at ${marker.offset} from original AttributionSpans to copy region.'); @@ -692,22 +649,18 @@ class AttributedSpans { .reversed // .where((marker) => marker.offset > endOffset!) // .forEach((marker) { - _log.log( - 'copyAttributionRegion', 'marker after the copy region: $marker'); + _log.log('copyAttributionRegion', 'marker after the copy region: $marker'); // Track any markers that end after the `endOffset` // and start before `endOffset`. if (marker.isEnd) { - _log.log('copyAttributionRegion', - 'remembering this marker to insert in copied region'); + _log.log('copyAttributionRegion', 'remembering this marker to insert in copied region'); foundEndMarkers.putIfAbsent(marker.attribution, () => 0); - foundEndMarkers[marker.attribution] = - foundEndMarkers[marker.attribution]! + 1; + foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! + 1; } else { _log.log('copyAttributionRegion', 'this marker counters an earlier one we found. We will not re-insert this marker in the copied region'); foundEndMarkers.putIfAbsent(marker.attribution, () => 0); - foundEndMarkers[marker.attribution] = - foundEndMarkers[marker.attribution]! - 1; + foundEndMarkers[marker.attribution] = foundEndMarkers[marker.attribution]! - 1; } }); @@ -740,9 +693,7 @@ class AttributedSpans { /// Changes all spans in this [AttributedSpans] by pushing /// them back by [offset] amount. void pushAttributionsBack(int offset) { - final pushedAttributions = _attributions - .map((marker) => marker.copyWith(offset: marker.offset + offset)) - .toList(); + final pushedAttributions = _attributions.map((marker) => marker.copyWith(offset: marker.offset + offset)).toList(); _attributions ..clear() ..addAll(pushedAttributions); @@ -757,23 +708,18 @@ class AttributedSpans { final contractedAttributions = []; // Add all the markers that are unchanged. - contractedAttributions - .addAll(_attributions.where((marker) => marker.offset < startOffset)); + contractedAttributions.addAll(_attributions.where((marker) => marker.offset < startOffset)); - _log.log('contractAttributions', - 'removing $count characters starting at $startOffset'); + _log.log('contractAttributions', 'removing $count characters starting at $startOffset'); final needToEndAttributions = {}; final needToStartAttributions = {}; _attributions - .where((marker) => - (startOffset <= marker.offset) && - (marker.offset < startOffset + count)) + .where((marker) => (startOffset <= marker.offset) && (marker.offset < startOffset + count)) .forEach((marker) { // Get rid of this marker and keep track of // any open-ended attributions that need to // be closed. - _log.log('contractAttributions', - 'removing ${marker.markerType} at ${marker.offset}'); + _log.log('contractAttributions', 'removing ${marker.markerType} at ${marker.offset}'); if (marker.isStart) { if (needToEndAttributions.contains(marker.attribution)) { // We've already removed an `end` marker so now @@ -870,8 +816,7 @@ class AttributedSpans { _log.log('collapseSpans', 'end points before change: $endPoints'); if (marker.offset > contentLength - 1) { - _log.log('collapseSpans', - 'this marker is beyond the desired content length. Breaking loop.'); + _log.log('collapseSpans', 'this marker is beyond the desired content length. Breaking loop.'); break; } @@ -886,8 +831,7 @@ class AttributedSpans { // then there won't be an `end` just before this // `start` point. Insert one. if (marker.offset > 0 && !endPoints.contains(marker.offset - 1)) { - _log.log('collapseSpans', - 'going back one and adding end point at: ${marker.offset - 1}'); + _log.log('collapseSpans', 'going back one and adding end point at: ${marker.offset - 1}'); endPoints.add(marker.offset - 1); } } @@ -902,10 +846,8 @@ class AttributedSpans { // the end of the content. We do this because we're not // guaranteed to have another `start` marker after this // `end` marker. - if (marker.offset < contentLength - 1 && - !startPoints.contains(marker.offset + 1)) { - _log.log('collapseSpans', - 'jumping forward one to add a start point at: ${marker.offset + 1}'); + if (marker.offset < contentLength - 1 && !startPoints.contains(marker.offset + 1)) { + _log.log('collapseSpans', 'jumping forward one to add a start point at: ${marker.offset + 1}'); startPoints.add(marker.offset + 1); } } @@ -934,10 +876,8 @@ class AttributedSpans { // Create the collapsed spans. List collapsedSpans = []; for (int i = 0; i < startPoints.length; ++i) { - _log.log('collapseSpans', - 'building span from ${startPoints[i]} -> ${endPoints[i]}'); - _log.log('collapseSpans', - ' - attributions in span: ${getAllAttributionsAt(startPoints[i])}'); + _log.log('collapseSpans', 'building span from ${startPoints[i]} -> ${endPoints[i]}'); + _log.log('collapseSpans', ' - attributions in span: ${getAllAttributionsAt(startPoints[i])}'); final attributionsAtOffset = getAllAttributionsAt(startPoints[i]); collapsedSpans.add(MultiAttributionSpan( @@ -955,16 +895,14 @@ class AttributedSpans { identical(this, other) || other is AttributedSpans && runtimeType == other.runtimeType && - const DeepCollectionEquality() - .equals(_attributions, other._attributions); + const DeepCollectionEquality().equals(_attributions, other._attributions); @override int get hashCode => _attributions.hashCode; @override String toString() { - final buffer = StringBuffer( - '[AttributedSpans] (${(_attributions.length / 2).round()} spans):'); + final buffer = StringBuffer('[AttributedSpans] (${(_attributions.length / 2).round()} spans):'); for (final marker in _attributions) { buffer.write('\n - $marker'); } @@ -1015,8 +953,7 @@ class SpanMarker implements Comparable { ); @override - String toString() => - '[SpanMarker] - attribution: $attribution, offset: $offset, type: $markerType'; + String toString() => '[SpanMarker] - attribution: $attribution, offset: $offset, type: $markerType'; @override int compareTo(SpanMarker other) { @@ -1032,8 +969,7 @@ class SpanMarker implements Comparable { markerType == other.markerType; @override - int get hashCode => - attribution.hashCode ^ offset.hashCode ^ markerType.hashCode; + int get hashCode => attribution.hashCode ^ offset.hashCode ^ markerType.hashCode; } /// The type of a marker within a span, either [start] or [end]. @@ -1161,10 +1097,7 @@ class NamedAttribution implements Attribution { @override bool operator ==(Object other) => - identical(this, other) || - other is NamedAttribution && - runtimeType == other.runtimeType && - id == other.id; + identical(this, other) || other is NamedAttribution && runtimeType == other.runtimeType && id == other.id; @override int get hashCode => id.hashCode; diff --git a/super_editor/lib/src/infrastructure/multi_tap_gesture.dart b/super_editor/lib/src/infrastructure/multi_tap_gesture.dart index 43363104da..585b1df8fe 100644 --- a/super_editor/lib/src/infrastructure/multi_tap_gesture.dart +++ b/super_editor/lib/src/infrastructure/multi_tap_gesture.dart @@ -295,7 +295,7 @@ class _TapTracker { required PointerDownEvent event, required this.entry, required Duration doubleTapMinTime, - }) : pointer = event.pointer, + }) : pointer = event.pointer, _initialGlobalPosition = event.position, initialButtons = event.buttons, _doubleTapMinTimeCountdown = _CountdownZoned(duration: doubleTapMinTime); diff --git a/super_editor/lib/src/infrastructure/super_selectable_text.dart b/super_editor/lib/src/infrastructure/super_selectable_text.dart index d79bf89771..3de81ff01d 100644 --- a/super_editor/lib/src/infrastructure/super_selectable_text.dart +++ b/super_editor/lib/src/infrastructure/super_selectable_text.dart @@ -104,8 +104,7 @@ class SuperSelectableText extends StatefulWidget { SuperSelectableTextState createState() => SuperSelectableTextState(); } -class SuperSelectableTextState extends State - implements TextLayout { +class SuperSelectableTextState extends State implements TextLayout { // [GlobalKey] that provides access to the [RenderParagraph] associated // with the text that this [SuperSelectableText] widget displays. final GlobalKey _textKey = GlobalKey(); @@ -135,9 +134,8 @@ class SuperSelectableTextState extends State _cachedTextLength = widget.richText.toPlainText().length; } - RenderParagraph? get _renderParagraph => _textKey.currentContext != null - ? _textKey.currentContext!.findRenderObject() as RenderParagraph - : null; + RenderParagraph? get _renderParagraph => + _textKey.currentContext != null ? _textKey.currentContext!.findRenderObject() as RenderParagraph : null; // TODO: use TextPainter line height when Flutter makes the info available. (#46) double get _lineHeight { @@ -177,12 +175,10 @@ class SuperSelectableTextState extends State @override Offset getOffsetAtPosition(TextPosition position) { if (_renderParagraph == null) { - throw Exception( - 'SelectableText does not yet have a RenderParagraph. Can\'t getOffsetForPosition().'); + throw Exception('SelectableText does not yet have a RenderParagraph. Can\'t getOffsetForPosition().'); } - if (_renderParagraph!.hasSize && - (kDebugMode && _renderParagraph!.debugNeedsLayout)) { + if (_renderParagraph!.hasSize && (kDebugMode && _renderParagraph!.debugNeedsLayout)) { // This condition was added because getOffsetForCaret() was throwing // an exception when debugNeedsLayout is true. It's unclear what we're // supposed to do at our level to ensure that condition doesn't happen @@ -201,8 +197,7 @@ class SuperSelectableTextState extends State @override List getBoxesForSelection(TextSelection selection) { if (_renderParagraph == null) { - throw Exception( - 'SelectableText does not yet have a RenderParagraph. Can\'t getBoxesForSelection().'); + throw Exception('SelectableText does not yet have a RenderParagraph. Can\'t getBoxesForSelection().'); } return _renderParagraph!.getBoxesForSelection(selection); @@ -231,9 +226,7 @@ class SuperSelectableTextState extends State final renderParagraph = _renderParagraph!; // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. - final positionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + - Offset(0, _lineHeight / 2); + final positionOffset = renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, _lineHeight / 2); final endOfLineOffset = Offset(0, positionOffset.dy); return renderParagraph.getPositionForOffset(endOfLineOffset); } @@ -247,11 +240,8 @@ class SuperSelectableTextState extends State final renderParagraph = _renderParagraph!; // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. - final positionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + - Offset(0, _lineHeight / 2); - final endOfLineOffset = - Offset(renderParagraph.size.width, positionOffset.dy); + final positionOffset = renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, _lineHeight / 2); + final endOfLineOffset = Offset(renderParagraph.size.width, positionOffset.dy); return renderParagraph.getPositionForOffset(endOfLineOffset); } @@ -266,8 +256,7 @@ class SuperSelectableTextState extends State // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. final currentSelectionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + - Offset(0, lineHeight / 2); + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, lineHeight / 2); final oneLineUpOffset = currentSelectionOffset - Offset(0, lineHeight); if (oneLineUpOffset.dy < 0) { @@ -289,8 +278,7 @@ class SuperSelectableTextState extends State // Note: add half the line height to the current offset to help deal with // line heights that aren't accurate. final currentSelectionOffset = - renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + - Offset(0, lineHeight / 2); + renderParagraph.getOffsetForCaret(currentPosition, Rect.zero) + Offset(0, lineHeight / 2); final oneLineDownOffset = currentSelectionOffset + Offset(0, lineHeight); if (oneLineDownOffset.dy > renderParagraph.size.height) { @@ -332,8 +320,7 @@ class SuperSelectableTextState extends State } @override - TextSelection expandSelection( - TextPosition position, TextExpansion expansion, TextAffinity affinity) { + TextSelection expandSelection(TextPosition position, TextExpansion expansion, TextAffinity affinity) { return expansion(widget.richText.toPlainText(), position, affinity); } @@ -368,8 +355,7 @@ class SuperSelectableTextState extends State } final renderParagraph = _renderParagraph!; - final contentOffset = renderParagraph.localToGlobal(Offset.zero, - ancestor: ancestorCoordinateSpace); + final contentOffset = renderParagraph.localToGlobal(Offset.zero, ancestor: ancestorCoordinateSpace); final textRect = contentOffset & renderParagraph.size; if (region.overlaps(textRect)) { @@ -530,10 +516,7 @@ class _TextSelectionPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - if (isTextEmpty && - highlightWhenEmpty && - selection.isCollapsed && - selection.extentOffset == 0) { + if (isTextEmpty && highlightWhenEmpty && selection.isCollapsed && selection.extentOffset == 0) { //&& highlightWhenEmpty) { // This is an empty paragraph, which is selected. Paint a small selection. canvas.drawRect( @@ -546,15 +529,12 @@ class _TextSelectionPainter extends CustomPainter { for (final box in selectionBoxes) { final rawRect = box.toRect(); - final rect = Rect.fromLTWH( - rawRect.left, rawRect.top - 2, rawRect.width, rawRect.height + 4); + final rect = Rect.fromLTWH(rawRect.left, rawRect.top - 2, rawRect.width, rawRect.height + 4); canvas.drawRect( // Note: If the rect has no width then we've selected an empty line. Give // that line a slight width for visibility. - rect.width > 0 - ? rect - : Rect.fromLTWH(rect.left, rect.top, 5, rect.height), + rect.width > 0 ? rect : Rect.fromLTWH(rect.left, rect.top, 5, rect.height), selectionPaint, ); } @@ -562,8 +542,7 @@ class _TextSelectionPainter extends CustomPainter { @override bool shouldRepaint(_TextSelectionPainter oldDelegate) { - return renderParagraph != oldDelegate.renderParagraph || - selection != oldDelegate.selection; + return renderParagraph != oldDelegate.renderParagraph || selection != oldDelegate.selection; } } @@ -625,8 +604,7 @@ class _BlinkingCaret extends StatefulWidget { _BlinkingCaretState createState() => _BlinkingCaretState(); } -class _BlinkingCaretState extends State<_BlinkingCaret> - with SingleTickerProviderStateMixin { +class _BlinkingCaretState extends State<_BlinkingCaret> with SingleTickerProviderStateMixin { // Controls the blinking caret animation. late _CaretBlinkController _caretBlinkController; @@ -690,8 +668,7 @@ class _CursorPainter extends CustomPainter { final int caretTextPosition; final double width; final BorderRadius borderRadius; - final double - lineHeight; // TODO: this should probably also come from the TextPainter (#46). + final double lineHeight; // TODO: this should probably also come from the TextPainter (#46). final bool isTextEmpty; final bool showCaret; final Color caretColor; @@ -709,14 +686,11 @@ class _CursorPainter extends CustomPainter { caretPaint.color = caretColor.withOpacity(blinkController.opacity); - final caretHeight = paragraph - .getFullHeightForCaret(TextPosition(offset: caretTextPosition)) ?? - lineHeight; + final caretHeight = paragraph.getFullHeightForCaret(TextPosition(offset: caretTextPosition)) ?? lineHeight; Offset caretOffset = isTextEmpty ? Offset(0, (lineHeight - caretHeight) / 2) - : paragraph.getOffsetForCaret( - TextPosition(offset: caretTextPosition), Rect.zero); + : paragraph.getOffsetForCaret(TextPosition(offset: caretTextPosition), Rect.zero); if (borderRadius == BorderRadius.zero) { canvas.drawRect( @@ -815,17 +789,14 @@ class DebugSelectableTextDecorator extends StatefulWidget { final bool showDebugPaint; @override - _DebugSelectableTextDecoratorState createState() => - _DebugSelectableTextDecoratorState(); + _DebugSelectableTextDecoratorState createState() => _DebugSelectableTextDecoratorState(); } -class _DebugSelectableTextDecoratorState - extends State { +class _DebugSelectableTextDecoratorState extends State { SuperSelectableTextState? get _selectableTextState => widget.selectableTextKey.currentState as SuperSelectableTextState?; - RenderParagraph? get _renderParagraph => - _selectableTextState?._renderParagraph; + RenderParagraph? get _renderParagraph => _selectableTextState?._renderParagraph; List _computeTextRectangles(RenderParagraph renderParagraph) { return renderParagraph @@ -862,8 +833,7 @@ class _DebugSelectableTextDecoratorState }); return const SizedBox(); } - if (_renderParagraph!.hasSize && - (kDebugMode && _renderParagraph!.debugNeedsLayout)) { + if (_renderParagraph!.hasSize && (kDebugMode && _renderParagraph!.debugNeedsLayout)) { // Schedule another frame so we can compute the debug paint. WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { setState(() {}); diff --git a/super_editor/lib/src/infrastructure/super_textfield.dart b/super_editor/lib/src/infrastructure/super_textfield.dart index 29863366a5..3190400223 100644 --- a/super_editor/lib/src/infrastructure/super_textfield.dart +++ b/super_editor/lib/src/infrastructure/super_textfield.dart @@ -95,8 +95,7 @@ class SuperTextFieldState extends State { final _selectableTextKey = GlobalKey(); final _textScrollKey = GlobalKey(); late FocusNode _focusNode; - bool _hasFocus = - false; // cache whether we have focus so we know when it changes + bool _hasFocus = false; // cache whether we have focus so we know when it changes late AttributedTextEditingController _controller; late ScrollController _scrollController; @@ -124,8 +123,7 @@ class SuperTextFieldState extends State { if (oldWidget.focusNode == null) { _focusNode.dispose(); } - _focusNode = (widget.focusNode ?? FocusNode()) - ..addListener(_onFocusChange); + _focusNode = (widget.focusNode ?? FocusNode())..addListener(_onFocusChange); _hasFocus = _focusNode.hasFocus; } @@ -166,8 +164,7 @@ class SuperTextFieldState extends State { // // This behavior matches Flutter's standard behavior. if (_focusNode.hasFocus && !_hasFocus) { - _controller.selection = - TextSelection.collapsed(offset: _controller.text.text.length); + _controller.selection = TextSelection.collapsed(offset: _controller.text.text.length); } _hasFocus = _focusNode.hasFocus; } @@ -188,12 +185,8 @@ class SuperTextFieldState extends State { final estimatedLineHeight = _getEstimatedLineHeight(); final estimatedLinesOfText = _getEstimatedLinesOfText(); final estimatedContentHeight = estimatedLinesOfText * estimatedLineHeight; - final minHeight = widget.minLines != null - ? widget.minLines! * estimatedLineHeight + widget.padding.vertical - : null; - final maxHeight = widget.maxLines != null - ? widget.maxLines! * estimatedLineHeight + widget.padding.vertical - : null; + final minHeight = widget.minLines != null ? widget.minLines! * estimatedLineHeight + widget.padding.vertical : null; + final maxHeight = widget.maxLines != null ? widget.maxLines! * estimatedLineHeight + widget.padding.vertical : null; double? viewportHeight; if (maxHeight != null && estimatedContentHeight > maxHeight) { viewportHeight = maxHeight; @@ -222,9 +215,8 @@ class SuperTextFieldState extends State { return 0; } - final offsetAtEndOfText = _selectableTextKey.currentState! - .getOffsetAtPosition( - TextPosition(offset: _controller.text.text.length)); + final offsetAtEndOfText = + _selectableTextKey.currentState!.getOffsetAtPosition(TextPosition(offset: _controller.text.text.length)); int lineCount = (offsetAtEndOfText.dy / _getEstimatedLineHeight()).ceil(); if (_controller.text.text.endsWith('\n')) { @@ -276,13 +268,8 @@ class SuperTextFieldState extends State { builder: (context) { final isTextEmpty = _controller.text.text.isEmpty; final showHint = widget.hintBuilder != null && - ((isTextEmpty && - widget.hintBehavior == - HintBehavior.displayHintUntilTextEntered) || - (isTextEmpty && - !_focusNode.hasFocus && - widget.hintBehavior == - HintBehavior.displayHintUntilFocus)); + ((isTextEmpty && widget.hintBehavior == HintBehavior.displayHintUntilTextEntered) || + (isTextEmpty && !_focusNode.hasFocus && widget.hintBehavior == HintBehavior.displayHintUntilFocus)); return _buildDecoration( child: SuperTextFieldScrollview( @@ -311,9 +298,7 @@ class SuperTextFieldState extends State { Widget _buildDecoration({ required Widget child, }) { - return widget.decorationBuilder != null - ? widget.decorationBuilder!(context, child) - : child; + return widget.decorationBuilder != null ? widget.decorationBuilder!(context, child) : child; } Widget _buildSelectableText() { @@ -394,12 +379,10 @@ class SuperTextFieldGestureInteractor extends StatefulWidget { final Widget child; @override - _SuperTextFieldGestureInteractorState createState() => - _SuperTextFieldGestureInteractorState(); + _SuperTextFieldGestureInteractorState createState() => _SuperTextFieldGestureInteractorState(); } -class _SuperTextFieldGestureInteractorState - extends State { +class _SuperTextFieldGestureInteractorState extends State { final _cursorStyle = ValueNotifier(SystemMouseCursors.basic); _SelectionType _selectionType = _SelectionType.position; @@ -414,8 +397,7 @@ class _SuperTextFieldGestureInteractorState SuperSelectableTextState get _text => widget.textKey.currentState!; - SuperTextFieldScrollviewState get _textScroll => - widget.textScrollKey.currentState!; + SuperTextFieldScrollviewState get _textScroll => widget.textScrollKey.currentState!; void _onTapDown(TapDownDetails details) { _log.log('_onTapDown', 'EditableDocument: onTapDown()'); @@ -424,10 +406,8 @@ class _SuperTextFieldGestureInteractorState final textOffset = _getTextOffset(details.localPosition); final tapTextPosition = _getPositionNearestToTextOffset(textOffset); - final expandSelection = RawKeyboard.instance.keysPressed - .contains(LogicalKeyboardKey.shiftLeft) || - RawKeyboard.instance.keysPressed - .contains(LogicalKeyboardKey.shiftRight) || + final expandSelection = RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft) || + RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftRight) || RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shift); setState(() { @@ -451,8 +431,7 @@ class _SuperTextFieldGestureInteractorState if (tapTextPosition != null) { setState(() { - widget.textController.selection = - _text.getWordSelectionAt(tapTextPosition); + widget.textController.selection = _text.getWordSelectionAt(tapTextPosition); }); } else { _clearSelection(); @@ -474,8 +453,7 @@ class _SuperTextFieldGestureInteractorState if (tapTextPosition != null) { setState(() { - widget.textController.selection = - _getParagraphSelectionAt(tapTextPosition, TextAffinity.downstream); + widget.textController.selection = _getParagraphSelectionAt(tapTextPosition, TextAffinity.downstream); }); } else { _clearSelection(); @@ -489,8 +467,7 @@ class _SuperTextFieldGestureInteractorState } void _onRightClick(TapUpDetails details) { - widget.onRightClick - ?.call(context, widget.textController, details.localPosition); + widget.onRightClick?.call(context, widget.textController, details.localPosition); } void _onPanStart(DragStartDetails details) { @@ -498,8 +475,7 @@ class _SuperTextFieldGestureInteractorState _dragStartInViewport = details.localPosition; _dragStartInText = _getTextOffset(_dragStartInViewport!); - _dragRectInViewport = - Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); + _dragRectInViewport = Rect.fromLTWH(_dragStartInViewport!.dx, _dragStartInViewport!.dy, 1, 1); widget.focusNode.requestFocus(); } @@ -509,8 +485,7 @@ class _SuperTextFieldGestureInteractorState setState(() { _dragEndInViewport = details.localPosition; _dragEndInText = _getTextOffset(_dragEndInViewport!); - _dragRectInViewport = - Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); + _dragRectInViewport = Rect.fromPoints(_dragStartInViewport!, _dragEndInViewport!); _log.log('_onPanUpdate', ' - drag rect: $_dragRectInViewport'); _updateCursorStyle(details.localPosition); _updateDragSelection(); @@ -547,19 +522,13 @@ class _SuperTextFieldGestureInteractorState } setState(() { - final startDragOffset = - _getPositionNearestToTextOffset(_dragStartInText!).offset; - final endDragOffset = - _getPositionNearestToTextOffset(_dragEndInText!).offset; - final affinity = startDragOffset <= endDragOffset - ? TextAffinity.downstream - : TextAffinity.upstream; + final startDragOffset = _getPositionNearestToTextOffset(_dragStartInText!).offset; + final endDragOffset = _getPositionNearestToTextOffset(_dragEndInText!).offset; + final affinity = startDragOffset <= endDragOffset ? TextAffinity.downstream : TextAffinity.upstream; if (_selectionType == _SelectionType.paragraph) { - final baseParagraphSelection = _getParagraphSelectionAt( - TextPosition(offset: startDragOffset), affinity); - final extentParagraphSelection = _getParagraphSelectionAt( - TextPosition(offset: endDragOffset), affinity); + final baseParagraphSelection = _getParagraphSelectionAt(TextPosition(offset: startDragOffset), affinity); + final extentParagraphSelection = _getParagraphSelectionAt(TextPosition(offset: endDragOffset), affinity); widget.textController.selection = _combineSelections( baseParagraphSelection, @@ -567,10 +536,8 @@ class _SuperTextFieldGestureInteractorState affinity, ); } else if (_selectionType == _SelectionType.word) { - final baseParagraphSelection = - _text.getWordSelectionAt(TextPosition(offset: startDragOffset)); - final extentParagraphSelection = - _text.getWordSelectionAt(TextPosition(offset: endDragOffset)); + final baseParagraphSelection = _text.getWordSelectionAt(TextPosition(offset: startDragOffset)); + final extentParagraphSelection = _text.getWordSelectionAt(TextPosition(offset: endDragOffset)); widget.textController.selection = _combineSelections( baseParagraphSelection, @@ -604,8 +571,7 @@ class _SuperTextFieldGestureInteractorState void _clearSelection() { setState(() { - widget.textController.selection = - const TextSelection.collapsed(offset: -1); + widget.textController.selection = const TextSelection.collapsed(offset: -1); }); } @@ -620,10 +586,8 @@ class _SuperTextFieldGestureInteractorState void _onPointerSignal(PointerSignalEvent event) { if (event is PointerScrollEvent) { // TODO: remove access to _textScroll.widget - final newScrollOffset = - (_textScroll.widget.scrollController.offset + event.scrollDelta.dy) - .clamp(0.0, - _textScroll.widget.scrollController.position.maxScrollExtent); + final newScrollOffset = (_textScroll.widget.scrollController.offset + event.scrollDelta.dy) + .clamp(0.0, _textScroll.widget.scrollController.position.maxScrollExtent); _textScroll.widget.scrollController.jumpTo(newScrollOffset); _updateDragSelection(); @@ -632,8 +596,7 @@ class _SuperTextFieldGestureInteractorState void _scrollIfNearBoundary() { if (_dragEndInViewport == null) { - _log.log('_scrollIfNearBoundary', - "Can't scroll near boundary because _dragEndInViewport is null"); + _log.log('_scrollIfNearBoundary', "Can't scroll near boundary because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -677,8 +640,7 @@ class _SuperTextFieldGestureInteractorState void _startScrollingToStart() { if (_dragEndInViewport == null) { - _log.log( - '_scrollUp', "Can't scroll up because _dragEndInViewport is null"); + _log.log('_scrollUp', "Can't scroll up because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } @@ -696,15 +658,13 @@ class _SuperTextFieldGestureInteractorState void _startScrollingToEnd() { if (_dragEndInViewport == null) { - _log.log('_scrollDown', - "Can't scroll down because _dragEndInViewport is null"); + _log.log('_scrollDown', "Can't scroll down because _dragEndInViewport is null"); assert(_dragEndInViewport != null); return; } final editorBox = context.findRenderObject() as RenderBox; - final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy) - .clamp(0.0, _dragGutterExtent); + final gutterAmount = (editorBox.size.height - _dragEndInViewport!.dy).clamp(0.0, _dragGutterExtent); final speedPercent = 1.0 - (gutterAmount / _dragGutterExtent); final scrollAmount = lerpDouble(0, _maxDragSpeed, speedPercent)!; @@ -725,18 +685,13 @@ class _SuperTextFieldGestureInteractorState TextPosition? _getPositionAtOffset(Offset textFieldOffset) { final textOffset = _getTextOffset(textFieldOffset); - final textBox = - widget.textKey.currentContext!.findRenderObject() as RenderBox; + final textBox = widget.textKey.currentContext!.findRenderObject() as RenderBox; - return textBox.size.contains(textOffset) - ? widget.textKey.currentState!.getPositionAtOffset(textOffset) - : null; + return textBox.size.contains(textOffset) ? widget.textKey.currentState!.getPositionAtOffset(textOffset) : null; } - TextSelection _getParagraphSelectionAt( - TextPosition textPosition, TextAffinity affinity) { - return _text.expandSelection( - textPosition, paragraphExpansionFilter, affinity); + TextSelection _getParagraphSelectionAt(TextPosition textPosition, TextAffinity affinity) { + return _text.expandSelection(textPosition, paragraphExpansionFilter, affinity); } TextPosition _getPositionNearestToTextOffset(Offset textOffset) { @@ -750,8 +705,7 @@ class _SuperTextFieldGestureInteractorState Offset _getTextOffset(Offset textFieldOffset) { final textFieldBox = context.findRenderObject() as RenderBox; - final textBox = - widget.textKey.currentContext!.findRenderObject() as RenderBox; + final textBox = widget.textKey.currentContext!.findRenderObject() as RenderBox; return textBox.globalToLocal(textFieldOffset, ancestor: textFieldBox); } @@ -765,8 +719,7 @@ class _SuperTextFieldGestureInteractorState child: RawGestureDetector( behavior: HitTestBehavior.translucent, gestures: { - TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers< - TapSequenceGestureRecognizer>( + TapSequenceGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => TapSequenceGestureRecognizer(), (TapSequenceGestureRecognizer recognizer) { recognizer @@ -777,8 +730,7 @@ class _SuperTextFieldGestureInteractorState ..onTripleTap = _onTripleTap; }, ), - PanGestureRecognizer: - GestureRecognizerFactoryWithHandlers( + PanGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => PanGestureRecognizer(), (PanGestureRecognizer recognizer) { recognizer @@ -858,12 +810,10 @@ class SuperTextFieldKeyboardInteractor extends StatefulWidget { final Widget child; @override - _SuperTextFieldKeyboardInteractorState createState() => - _SuperTextFieldKeyboardInteractorState(); + _SuperTextFieldKeyboardInteractorState createState() => _SuperTextFieldKeyboardInteractorState(); } -class _SuperTextFieldKeyboardInteractorState - extends State { +class _SuperTextFieldKeyboardInteractorState extends State { KeyEventResult _onKeyPressed(FocusNode focusNode, RawKeyEvent keyEvent) { _log.log('_onKeyPressed', 'keyEvent: ${keyEvent.character}'); if (keyEvent is! RawKeyDownEvent) { @@ -871,11 +821,9 @@ class _SuperTextFieldKeyboardInteractorState return KeyEventResult.ignored; } - TextFieldKeyboardHandlerResult instruction = - TextFieldKeyboardHandlerResult.notHandled; + TextFieldKeyboardHandlerResult instruction = TextFieldKeyboardHandlerResult.notHandled; int index = 0; - while (instruction == TextFieldKeyboardHandlerResult.notHandled && - index < widget.keyboardActions.length) { + while (instruction == TextFieldKeyboardHandlerResult.notHandled && index < widget.keyboardActions.length) { instruction = widget.keyboardActions[index]( controller: widget.textController, selectableTextState: widget.textKey.currentState!, @@ -884,9 +832,7 @@ class _SuperTextFieldKeyboardInteractorState index += 1; } - return instruction == TextFieldKeyboardHandlerResult.handled - ? KeyEventResult.handled - : KeyEventResult.ignored; + return instruction == TextFieldKeyboardHandlerResult.handled ? KeyEventResult.handled : KeyEventResult.ignored; } @override @@ -951,12 +897,10 @@ class SuperTextFieldScrollview extends StatefulWidget { final Widget child; @override - SuperTextFieldScrollviewState createState() => - SuperTextFieldScrollviewState(); + SuperTextFieldScrollviewState createState() => SuperTextFieldScrollviewState(); } -class SuperTextFieldScrollviewState extends State - with SingleTickerProviderStateMixin { +class SuperTextFieldScrollviewState extends State with SingleTickerProviderStateMixin { bool _scrollToStartOnTick = false; bool _scrollToEndOnTick = false; double _scrollAmountPerFrame = 0; @@ -1028,21 +972,14 @@ class SuperTextFieldScrollviewState extends State const gutterExtent = 0; // _dragGutterExtent final myBox = context.findRenderObject() as RenderBox; - final beyondLeftExtent = - min(extentOffset.dx - widget.scrollController.offset - gutterExtent, 0) - .abs(); + final beyondLeftExtent = min(extentOffset.dx - widget.scrollController.offset - gutterExtent, 0).abs(); final beyondRightExtent = max( - extentOffset.dx - - myBox.size.width - - widget.scrollController.offset + - gutterExtent + - widget.padding.horizontal, + extentOffset.dx - myBox.size.width - widget.scrollController.offset + gutterExtent + widget.padding.horizontal, 0); if (beyondLeftExtent > 0) { - final newScrollPosition = - (widget.scrollController.offset - beyondLeftExtent) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = (widget.scrollController.offset - beyondLeftExtent) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1050,9 +987,8 @@ class SuperTextFieldScrollviewState extends State curve: Curves.easeOut, ); } else if (beyondRightExtent > 0) { - final newScrollPosition = - (beyondRightExtent + widget.scrollController.offset) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = (beyondRightExtent + widget.scrollController.offset) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1071,39 +1007,29 @@ class SuperTextFieldScrollviewState extends State final extentOffset = _text.getOffsetAtPosition(selection.extent); const gutterExtent = 0; // _dragGutterExtent - final extentLineIndex = - (extentOffset.dy / widget.estimatedLineHeight).round(); + final extentLineIndex = (extentOffset.dy / widget.estimatedLineHeight).round(); final myBox = context.findRenderObject() as RenderBox; - final beyondTopExtent = min( - extentOffset.dy - widget.scrollController.offset - gutterExtent, 0) - .abs(); + final beyondTopExtent = min(extentOffset.dy - widget.scrollController.offset - gutterExtent, 0).abs(); final beyondBottomExtent = max( ((extentLineIndex + 1) * widget.estimatedLineHeight) - myBox.size.height - widget.scrollController.offset + gutterExtent + - (widget.estimatedLineHeight / - 2) + // manual adjustment to avoid line getting half cut off + (widget.estimatedLineHeight / 2) + // manual adjustment to avoid line getting half cut off widget.padding.vertical / 2, 0); _log.log('_ensureSelectionExtentIsVisible', 'Ensuring extent is visible.'); - _log.log('_ensureSelectionExtentIsVisible', - ' - interaction size: ${myBox.size}'); - _log.log('_ensureSelectionExtentIsVisible', - ' - scroll extent: ${widget.scrollController.offset}'); - _log.log( - '_ensureSelectionExtentIsVisible', ' - extent rect: $extentOffset'); - _log.log( - '_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); - _log.log('_ensureSelectionExtentIsVisible', - ' - beyond bottom: $beyondBottomExtent'); + _log.log('_ensureSelectionExtentIsVisible', ' - interaction size: ${myBox.size}'); + _log.log('_ensureSelectionExtentIsVisible', ' - scroll extent: ${widget.scrollController.offset}'); + _log.log('_ensureSelectionExtentIsVisible', ' - extent rect: $extentOffset'); + _log.log('_ensureSelectionExtentIsVisible', ' - beyond top: $beyondTopExtent'); + _log.log('_ensureSelectionExtentIsVisible', ' - beyond bottom: $beyondBottomExtent'); if (beyondTopExtent > 0) { - final newScrollPosition = - (widget.scrollController.offset - beyondTopExtent) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = (widget.scrollController.offset - beyondTopExtent) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1111,9 +1037,8 @@ class SuperTextFieldScrollviewState extends State curve: Curves.easeOut, ); } else if (beyondBottomExtent > 0) { - final newScrollPosition = - (beyondBottomExtent + widget.scrollController.offset) - .clamp(0.0, widget.scrollController.position.maxScrollExtent); + final newScrollPosition = (beyondBottomExtent + widget.scrollController.offset) + .clamp(0.0, widget.scrollController.position.maxScrollExtent); widget.scrollController.animateTo( newScrollPosition, @@ -1150,8 +1075,7 @@ class SuperTextFieldScrollviewState extends State return; } - widget.scrollController.position - .jumpTo(widget.scrollController.offset - _scrollAmountPerFrame); + widget.scrollController.position.jumpTo(widget.scrollController.offset - _scrollAmountPerFrame); } void _startScrollingToEnd({required double amountPerFrame}) { @@ -1177,13 +1101,11 @@ class SuperTextFieldScrollviewState extends State } void _scrollToEnd() { - if (widget.scrollController.offset >= - widget.scrollController.position.maxScrollExtent) { + if (widget.scrollController.offset >= widget.scrollController.position.maxScrollExtent) { return; } - widget.scrollController.position - .jumpTo(widget.scrollController.offset + _scrollAmountPerFrame); + widget.scrollController.position.jumpTo(widget.scrollController.offset + _scrollAmountPerFrame); } void _onTick(elapsedTime) { @@ -1212,8 +1134,8 @@ class SuperTextFieldScrollviewState extends State } } -typedef RightClickListener = void Function(BuildContext textFieldContext, - AttributedTextEditingController textController, Offset textFieldOffset); +typedef RightClickListener = void Function( + BuildContext textFieldContext, AttributedTextEditingController textController, Offset textFieldOffset); enum _SelectionType { /// The selection bound is set on a per-character basis. @@ -1283,10 +1205,8 @@ const defaultTextFieldKeyboardHandlers = [ DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed, DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed, DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys, - DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed, - DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed, + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed, + DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed, DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed, DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed, ]; @@ -1369,8 +1289,7 @@ class DefaultSuperTextFieldKeyboardHandlers { } if (keyEvent.logicalKey == LogicalKeyboardKey.arrowLeft) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling left arrow key'); final movementModifiers = { 'movement_unit': 'character', @@ -1388,8 +1307,7 @@ class DefaultSuperTextFieldKeyboardHandlers { movementModifiers: movementModifiers, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowRight) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling right arrow key'); + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling right arrow key'); final movementModifiers = { 'movement_unit': 'character', @@ -1407,16 +1325,14 @@ class DefaultSuperTextFieldKeyboardHandlers { movementModifiers: movementModifiers, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowUp) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling up arrow key'); controller.moveCaretVertically( selectableTextState: selectableTextState, expandSelection: keyEvent.isShiftPressed, moveUp: true, ); } else if (keyEvent.logicalKey == LogicalKeyboardKey.arrowDown) { - _log.log( - 'moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); + _log.log('moveUpDownLeftAndRightWithArrowKeys', ' - handling down arrow key'); controller.moveCaretVertically( selectableTextState: selectableTextState, expandSelection: keyEvent.isShiftPressed, @@ -1460,14 +1376,12 @@ class DefaultSuperTextFieldKeyboardHandlers { return TextFieldKeyboardHandlerResult.handled; } - static TextFieldKeyboardHandlerResult - deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed({ + static TextFieldKeyboardHandlerResult deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed({ required AttributedTextEditingController controller, required SuperSelectableTextState selectableTextState, required RawKeyEvent keyEvent, }) { - if (!keyEvent.isPrimaryShortcutKeyPressed || - keyEvent.logicalKey != LogicalKeyboardKey.backspace) { + if (!keyEvent.isPrimaryShortcutKeyPressed || keyEvent.logicalKey != LogicalKeyboardKey.backspace) { return TextFieldKeyboardHandlerResult.notHandled; } if (!controller.selection.isCollapsed) { @@ -1476,21 +1390,17 @@ class DefaultSuperTextFieldKeyboardHandlers { if (controller.selection.extentOffset < 0) { return TextFieldKeyboardHandlerResult.notHandled; } - if (selectableTextState - .getPositionAtStartOfLine(controller.selection.extent) - .offset == + if (selectableTextState.getPositionAtStartOfLine(controller.selection.extent).offset == controller.selection.extentOffset) { return TextFieldKeyboardHandlerResult.notHandled; } - controller.deleteTextOnLineBeforeCaret( - selectableTextState: selectableTextState); + controller.deleteTextOnLineBeforeCaret(selectableTextState: selectableTextState); return TextFieldKeyboardHandlerResult.handled; } - static TextFieldKeyboardHandlerResult - deleteTextWhenBackspaceOrDeleteIsPressed({ + static TextFieldKeyboardHandlerResult deleteTextWhenBackspaceOrDeleteIsPressed({ required AttributedTextEditingController controller, SuperSelectableTextState? selectableTextState, required RawKeyEvent keyEvent, @@ -1505,8 +1415,7 @@ class DefaultSuperTextFieldKeyboardHandlers { } if (controller.selection.isCollapsed) { - controller.deleteCharacter( - isBackspace ? TextAffinity.upstream : TextAffinity.downstream); + controller.deleteCharacter(isBackspace ? TextAffinity.upstream : TextAffinity.downstream); } else { controller.deleteSelectedText(); } @@ -1559,12 +1468,8 @@ class AttributedTextEditingController with ChangeNotifier { // the end of the new text value if (_selection.end > _text.text.length) { _selection = _selection.copyWith( - baseOffset: _selection.affinity == TextAffinity.downstream - ? _selection.baseOffset - : _text.text.length, - extentOffset: _selection.affinity == TextAffinity.downstream - ? _text.text.length - : _selection.extentOffset, + baseOffset: _selection.affinity == TextAffinity.downstream ? _selection.baseOffset : _text.text.length, + extentOffset: _selection.affinity == TextAffinity.downstream ? _text.text.length : _selection.extentOffset, ); } @@ -1582,8 +1487,7 @@ class AttributedTextEditingController with ChangeNotifier { } bool isSelectionWithinTextBounds(TextSelection selection) { - return selection.start <= text.text.length && - selection.end <= text.text.length; + return selection.start <= text.text.length && selection.end <= text.text.length; } TextSpan buildTextSpan(AttributionStyleBuilder styleBuilder) { @@ -1653,18 +1557,13 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { // extent to the left side of the selection. newExtent = selection.start; } else if (movementModifiers['movement_unit'] == 'line') { - newExtent = selectableTextState - .getPositionAtStartOfLine( - TextPosition(offset: selection.extentOffset)) - .offset; + newExtent = selectableTextState.getPositionAtStartOfLine(TextPosition(offset: selection.extentOffset)).offset; } else if (movementModifiers['movement_unit'] == 'word') { final plainText = text.text; newExtent = selection.extentOffset; newExtent -= 1; // we always want to jump at least 1 character. - while (newExtent > 0 && - plainText[newExtent - 1] != ' ' && - plainText[newExtent - 1] != '\n') { + while (newExtent > 0 && plainText[newExtent - 1] != ' ' && plainText[newExtent - 1] != '\n') { newExtent -= 1; } } else { @@ -1682,15 +1581,13 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { // extent to the left side of the selection. newExtent = selection.end; } else if (movementModifiers['movement_unit'] == 'line') { - final endOfLine = selectableTextState.getPositionAtEndOfLine( - TextPosition(offset: selection.extentOffset)); + final endOfLine = selectableTextState.getPositionAtEndOfLine(TextPosition(offset: selection.extentOffset)); final endPosition = TextPosition(offset: text.text.length); final plainText = text.text; // Note: we compare offset values because we don't care if the affinitys are equal - final isAutoWrapLine = endOfLine.offset != endPosition.offset && - (plainText[endOfLine.offset] != '\n'); + final isAutoWrapLine = endOfLine.offset != endPosition.offset && (plainText[endOfLine.offset] != '\n'); // Note: For lines that auto-wrap, moving the cursor to `offset` causes the // cursor to jump to the next line because the cursor is placed after @@ -1711,9 +1608,7 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { newExtent = extentPosition.offset; newExtent += 1; // we always want to jump at least 1 character. - while (newExtent < plainText.length && - plainText[newExtent] != ' ' && - plainText[newExtent] != '\n') { + while (newExtent < plainText.length && plainText[newExtent] != ' ' && plainText[newExtent] != '\n') { newExtent += 1; } } else { @@ -1735,15 +1630,13 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { int? newExtent; if (moveUp) { - newExtent = - selectableTextState.getPositionOneLineUp(selection.extent)?.offset; + newExtent = selectableTextState.getPositionOneLineUp(selection.extent)?.offset; // If there is no line above the current selection, move selection // to the beginning of the available text. newExtent ??= 0; } else { - newExtent = - selectableTextState.getPositionOneLineDown(selection.extent)?.offset; + newExtent = selectableTextState.getPositionOneLineDown(selection.extent)?.offset; // If there is no line below the current selection, move selection // to the end of the available text. @@ -1762,8 +1655,7 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { final existingAttributions = text.getAllAttributionsAt(initialTextOffset); if (!selection.isCollapsed) { - text = text.removeRegion( - startOffset: selection.start, endOffset: selection.end); + text = text.removeRegion(startOffset: selection.start, endOffset: selection.end); selection = TextSelection.collapsed(offset: selection.start); } @@ -1804,8 +1696,7 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { }) { assert(selection.isCollapsed); - final startOfLinePosition = - selectableTextState.getPositionAtStartOfLine(selection.extent); + final startOfLinePosition = selectableTextState.getPositionAtStartOfLine(selection.extent); selection = TextSelection( baseOffset: selection.extentOffset, extentOffset: startOfLinePosition.offset, @@ -1836,7 +1727,6 @@ extension DefaultSuperTextFieldActions on AttributedTextEditingController { textToInsert: '\n', startOffset: currentSelectionExtent.offset, ); - selection = - TextSelection.collapsed(offset: currentSelectionExtent.offset + 1); + selection = TextSelection.collapsed(offset: currentSelectionExtent.offset + 1); } } diff --git a/super_editor/lib/src/infrastructure/text_layout.dart b/super_editor/lib/src/infrastructure/text_layout.dart index 097550021f..a43c722742 100644 --- a/super_editor/lib/src/infrastructure/text_layout.dart +++ b/super_editor/lib/src/infrastructure/text_layout.dart @@ -61,19 +61,15 @@ abstract class TextLayout { /// Returns a [TextSelection] that surrounds the given [startingPosition] and expands /// outward until the given [expansion] chooses to stop expanding. - TextSelection expandSelection(TextPosition startingPosition, - TextExpansion expansion, TextAffinity affinity); + TextSelection expandSelection(TextPosition startingPosition, TextExpansion expansion, TextAffinity affinity); } -typedef TextExpansion = TextSelection Function( - String text, TextPosition startingPosition, TextAffinity affinity); +typedef TextExpansion = TextSelection Function(String text, TextPosition startingPosition, TextAffinity affinity); -TextSelection paragraphExpansionFilter( - String text, TextPosition startingPosition, TextAffinity affinity) { +TextSelection paragraphExpansionFilter(String text, TextPosition startingPosition, TextAffinity affinity) { // If the given position falls directly on a newline then return // just the newline character as the paragraph selection. - if (startingPosition.offset < text.length && - text[startingPosition.offset] == '\n') { + if (startingPosition.offset < text.length && text[startingPosition.offset] == '\n') { return TextSelection.collapsed(offset: startingPosition.offset); } diff --git a/super_editor/lib/src/serialization/markdown.dart b/super_editor/lib/src/serialization/markdown.dart index e60694fca6..aed0891b6e 100644 --- a/super_editor/lib/src/serialization/markdown.dart +++ b/super_editor/lib/src/serialization/markdown.dart @@ -155,8 +155,7 @@ class _MarkdownToDocument implements md.NodeVisitor { break; case 'li': if (_listItemTypeStack.isEmpty) { - throw Exception( - 'Tried to parse a markdown list item but the list item type was null'); + throw Exception('Tried to parse a markdown list item but the list item type was null'); } _addListItem( @@ -364,8 +363,7 @@ class _InlineMarkdownToDocument implements md.NodeVisitor { @override void visitText(md.Text text) { final attributedText = _textStack.removeLast(); - _textStack - .add(attributedText.copyAndAppend(AttributedText(text: text.text))); + _textStack.add(attributedText.copyAndAppend(AttributedText(text: text.text))); } @override @@ -405,18 +403,11 @@ extension on AttributedText { /// Serializes style attributions into markdown syntax in a repeatable /// order such that opening and closing styles match each other on /// the opening and closing ends of a span. - static String _sortAndSerializeAttributions( - Set attributions, AttributionVisitEvent event) { - const startOrder = [ - codeAttribution, - boldAttribution, - italicsAttribution, - strikethroughAttribution - ]; + static String _sortAndSerializeAttributions(Set attributions, AttributionVisitEvent event) { + const startOrder = [codeAttribution, boldAttribution, italicsAttribution, strikethroughAttribution]; final buffer = StringBuffer(); - final encodingOrder = - event == AttributionVisitEvent.start ? startOrder : startOrder.reversed; + final encodingOrder = event == AttributionVisitEvent.start ? startOrder : startOrder.reversed; for (final markdownStyleAttribution in encodingOrder) { if (attributions.contains(markdownStyleAttribution)) { @@ -456,9 +447,7 @@ extension on AttributedText { case AttributionVisitEvent.end: // +1 on end index because this visitor has inclusive indices // whereas substring() expects an exclusive ending index. - buffer - ..write(fullText.text.substring(spanStart, index + 1)) - ..write(markdownStyles); + buffer..write(fullText.text.substring(spanStart, index + 1))..write(markdownStyles); break; } }); diff --git a/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart b/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart index ff3fe8e93e..5589bc737c 100644 --- a/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart +++ b/super_editor/test/editor_smoke_tests/editor_entry_smoke_test.dart @@ -35,8 +35,7 @@ void main() { ); final composer = DocumentComposer(); final layoutKey = GlobalKey(); - DocumentLayout documentLayoutResolver() => - layoutKey.currentState as DocumentLayout; + DocumentLayout documentLayoutResolver() => layoutKey.currentState as DocumentLayout; final commonOps = CommonEditorOperations( editor: documentEditor, composer: composer, @@ -92,8 +91,7 @@ void main() { // print(' - $node'); // } - expect( - documentEditor.document.hasEquivalentContent(expectedDocument), true); + expect(documentEditor.document.hasEquivalentContent(expectedDocument), true); }); }); } diff --git a/super_editor/test/serialization/markdown_test.dart b/super_editor/test/serialization/markdown_test.dart index 4bd26effde..1a37d7118a 100644 --- a/super_editor/test/serialization/markdown_test.dart +++ b/super_editor/test/serialization/markdown_test.dart @@ -15,28 +15,22 @@ void main() { ), ]); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header1Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header1Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '# My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header2Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header2Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '## My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header3Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header3Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header4Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header4Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '#### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header5Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header5Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '##### My Header'); - (doc.nodes[0] as ParagraphNode).metadata['blockType'] = - header6Attribution; + (doc.nodes[0] as ParagraphNode).metadata['blockType'] = header6Attribution; expect(serializeDocumentToMarkdown(doc).trim(), '###### My Header'); }); @@ -48,14 +42,8 @@ void main() { text: 'My Header', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 3, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 8, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 3, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), @@ -75,8 +63,7 @@ void main() { ), ]); - expect( - serializeDocumentToMarkdown(doc).trim(), '> This is a blockquote'); + expect(serializeDocumentToMarkdown(doc).trim(), '> This is a blockquote'); }); test('blockquote with styles', () { @@ -87,14 +74,8 @@ void main() { text: 'This is a blockquote', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 10, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 19, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 10, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 19, markerType: SpanMarkerType.end), ], ), ), @@ -102,8 +83,7 @@ void main() { ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), - '> This is a **blockquote**'); + expect(serializeDocumentToMarkdown(doc).trim(), '> This is a **blockquote**'); }); test('code', () { @@ -143,22 +123,15 @@ This is some code text: 'This is a paragraph.', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 5, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 8, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), - 'This **is a** paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), 'This **is a** paragraph.'); }); test('paragraph with overlapping bold and italics', () { @@ -169,30 +142,17 @@ This is some code text: 'This is a paragraph.', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 5, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 8, - markerType: SpanMarkerType.end), - const SpanMarker( - attribution: italicsAttribution, - offset: 5, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: italicsAttribution, - offset: 8, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker(attribution: italicsAttribution, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: italicsAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), - 'This ***is a*** paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), 'This ***is a*** paragraph.'); }); test('paragraph with overlapping code and bold', () { @@ -203,30 +163,17 @@ This is some code text: 'This is a paragraph.', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: boldAttribution, - offset: 5, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: boldAttribution, - offset: 8, - markerType: SpanMarkerType.end), - SpanMarker( - attribution: codeAttribution, - offset: 5, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: codeAttribution, - offset: 8, - markerType: SpanMarkerType.end), + SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), + SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + SpanMarker(attribution: codeAttribution, offset: 5, markerType: SpanMarkerType.start), + SpanMarker(attribution: codeAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), - 'This `**is a**` paragraph.'); + expect(serializeDocumentToMarkdown(doc).trim(), 'This `**is a**` paragraph.'); }); test('image', () { @@ -238,8 +185,7 @@ This is some code ), ]); - expect(serializeDocumentToMarkdown(doc).trim(), - '![some alt text](https://someimage.com/the/image.png)'); + expect(serializeDocumentToMarkdown(doc).trim(), '![some alt text](https://someimage.com/the/image.png)'); }); test('horizontal rule', () { @@ -303,14 +249,8 @@ This is some code text: 'Unordered 1', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 0, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 8, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), @@ -371,14 +311,8 @@ This is some code text: 'Ordered 1', spans: AttributedSpans( attributions: [ - const SpanMarker( - attribution: boldAttribution, - offset: 0, - markerType: SpanMarkerType.start), - const SpanMarker( - attribution: boldAttribution, - offset: 6, - markerType: SpanMarkerType.end), + const SpanMarker(attribution: boldAttribution, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 6, markerType: SpanMarkerType.end), ], ), ), @@ -460,33 +394,26 @@ This is some code group('deserialization', () { test('headers', () { final header1Doc = deserializeMarkdownToDocument('# Header 1'); - expect((header1Doc.nodes.first as ParagraphNode).metadata['blockType'], - header1Attribution); + expect((header1Doc.nodes.first as ParagraphNode).metadata['blockType'], header1Attribution); final header2Doc = deserializeMarkdownToDocument('## Header 2'); - expect((header2Doc.nodes.first as ParagraphNode).metadata['blockType'], - header2Attribution); + expect((header2Doc.nodes.first as ParagraphNode).metadata['blockType'], header2Attribution); final header3Doc = deserializeMarkdownToDocument('### Header 3'); - expect((header3Doc.nodes.first as ParagraphNode).metadata['blockType'], - header3Attribution); + expect((header3Doc.nodes.first as ParagraphNode).metadata['blockType'], header3Attribution); final header4Doc = deserializeMarkdownToDocument('#### Header 4'); - expect((header4Doc.nodes.first as ParagraphNode).metadata['blockType'], - header4Attribution); + expect((header4Doc.nodes.first as ParagraphNode).metadata['blockType'], header4Attribution); final header5Doc = deserializeMarkdownToDocument('##### Header 5'); - expect((header5Doc.nodes.first as ParagraphNode).metadata['blockType'], - header5Attribution); + expect((header5Doc.nodes.first as ParagraphNode).metadata['blockType'], header5Attribution); final header6Doc = deserializeMarkdownToDocument('###### Header 6'); - expect((header6Doc.nodes.first as ParagraphNode).metadata['blockType'], - header6Attribution); + expect((header6Doc.nodes.first as ParagraphNode).metadata['blockType'], header6Attribution); }); test('blockquote', () { - final blockquoteDoc = - deserializeMarkdownToDocument('> This is a blockquote'); + final blockquoteDoc = deserializeMarkdownToDocument('> This is a blockquote'); final blockquote = blockquoteDoc.nodes.first as ParagraphNode; expect(blockquote.metadata['blockType'], blockquoteAttribution); @@ -505,8 +432,7 @@ This is some code }); test('image', () { - final codeBlockDoc = deserializeMarkdownToDocument( - '![Image alt text](https://images.com/some/image.png)'); + final codeBlockDoc = deserializeMarkdownToDocument('![Image alt text](https://images.com/some/image.png)'); final image = codeBlockDoc.nodes.first as ImageNode; expect(image.imageUrl, 'https://images.com/some/image.png'); @@ -522,8 +448,7 @@ This is some code expect(document.nodes.first, isA()); final paragraph = document.nodes.first as ParagraphNode; - expect(paragraph.text.text, - 'This is some unstyled text to parse as markdown'); + expect(paragraph.text.text, 'This is some unstyled text to parse as markdown'); }); test('single styled paragraph', () { @@ -536,17 +461,11 @@ This is some code final paragraph = document.nodes.first as ParagraphNode; final styledText = paragraph.text; - expect( - styledText.text, 'This is some styled text to parse as markdown'); + expect(styledText.text, 'This is some styled text to parse as markdown'); expect(styledText.getAllAttributionsAt(0).isEmpty, true); - expect( - styledText.getAllAttributionsAt(8).contains(boldAttribution), true); - expect( - styledText - .getAllAttributionsAt(13) - .containsAll([boldAttribution, italicsAttribution]), - true); + expect(styledText.getAllAttributionsAt(8).contains(boldAttribution), true); + expect(styledText.getAllAttributionsAt(13).containsAll([boldAttribution, italicsAttribution]), true); expect(styledText.getAllAttributionsAt(19).isEmpty, true); }); @@ -602,8 +521,7 @@ This is some code expect(document.nodes.length, 18); expect(document.nodes[0], isA()); - expect((document.nodes[0] as ParagraphNode).metadata['blockType'], - header1Attribution); + expect((document.nodes[0] as ParagraphNode).metadata['blockType'], header1Attribution); expect(document.nodes[1], isA()); diff --git a/super_editor/test/src/default_editor/attributions_test.dart b/super_editor/test/src/default_editor/attributions_test.dart index 4e2c76754f..3360424ac7 100644 --- a/super_editor/test/src/default_editor/attributions_test.dart +++ b/super_editor/test/src/default_editor/attributions_test.dart @@ -34,8 +34,7 @@ void main() { text: 'one two three', ); - final linkAttribution = - LinkAttribution(url: Uri.parse('https://flutter.dev')); + final linkAttribution = LinkAttribution(url: Uri.parse('https://flutter.dev')); // Add link across "one two" text.addAttribution( @@ -48,10 +47,7 @@ void main() { const TextRange(start: 4, end: 12), ); - expect( - text.spans.hasAttributionsWithin( - attributions: {linkAttribution}, start: 0, end: 12), - true); + expect(text.spans.hasAttributionsWithin(attributions: {linkAttribution}, start: 0, end: 12), true); }); }); }); diff --git a/super_editor/test/src/default_editor/document_keyboard_actions_test.dart b/super_editor/test/src/default_editor/document_keyboard_actions_test.dart index b709a98e80..a9fbd74fca 100644 --- a/super_editor/test/src/default_editor/document_keyboard_actions_test.dart +++ b/super_editor/test/src/default_editor/document_keyboard_actions_test.dart @@ -27,8 +27,7 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = - createEditContext(document: MutableDocument()); + final _editContext = createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, keyEvent: const FakeRawKeyEvent( @@ -54,8 +53,7 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = - createEditContext(document: MutableDocument()); + final _editContext = createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, keyEvent: const FakeRawKeyEvent( @@ -81,8 +79,7 @@ void main() { () { Platform.setTestInstance(MacPlatform()); - final _editContext = - createEditContext(document: MutableDocument()); + final _editContext = createEditContext(document: MutableDocument()); var result = selectAllWhenCmdAIsPressed( editContext: _editContext, keyEvent: const FakeRawKeyEvent( @@ -142,8 +139,7 @@ void main() { _editContext.composer.selection!.extent, const DocumentPosition( nodeId: 'paragraph', - nodePosition: - TextNodePosition(offset: 'This is some text'.length), + nodePosition: TextNodePosition(offset: 'This is some text'.length), ), ); @@ -194,8 +190,7 @@ void main() { _editContext.composer.selection!.extent, const DocumentPosition( nodeId: 'paragraph_2', - nodePosition: - TextNodePosition(offset: 'This is some text'.length), + nodePosition: TextNodePosition(offset: 'This is some text'.length), ), ); diff --git a/super_editor/test/src/default_editor/text_test.dart b/super_editor/test/src/default_editor/text_test.dart index 7d149240fd..d39ff6d91c 100644 --- a/super_editor/test/src/default_editor/text_test.dart +++ b/super_editor/test/src/default_editor/text_test.dart @@ -223,8 +223,7 @@ void main() { result = anyCharacterToInsertInTextContent( editContext: editContext, keyEvent: const FakeRawKeyEvent( - character: - '', // Empirically, pressing enter sends '' as the character instead of null + character: '', // Empirically, pressing enter sends '' as the character instead of null data: FakeRawKeyEventData( logicalKey: LogicalKeyboardKey.enter, physicalKey: PhysicalKeyboardKey.enter, diff --git a/super_editor/test/src/infrastructure/attributed_spans_test.dart b/super_editor/test/src/infrastructure/attributed_spans_test.dart index 7842e7603e..ad92b53c61 100644 --- a/super_editor/test/src/infrastructure/attributed_spans_test.dart +++ b/super_editor/test/src/infrastructure/attributed_spans_test.dart @@ -10,10 +10,8 @@ void main() { group('Spans', () { group('attribution queries', () { test('it expands a span from a given offset', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 3, end: 16); - final expandedSpan = - spans.expandAttributionToSpan(attribution: bold, offset: 6); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 3, end: 16); + final expandedSpan = spans.expandAttributionToSpan(attribution: bold, offset: 6); expect( expandedSpan, @@ -84,8 +82,7 @@ void main() { }); test('it returns spans that completely cover the range', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 0, end: 10); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 10); final attributionSpans = spans.getAttributionSpansInRange( attributionFilter: (attribution) => attribution == bold, start: 3, @@ -140,8 +137,7 @@ void main() { }); test('it resizes spans that completely cover the range', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 0, end: 10); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 10); final attributionSpans = spans.getAttributionSpansInRange( attributionFilter: (attribution) => attribution == bold, start: 3, @@ -165,165 +161,114 @@ void main() { group('single attribution', () { test('applies attribution to full span', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 0, end: 16); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 16); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), true); }); test('applies attribution to beginning of span', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 0, end: 7); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 0, end: 7); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 7), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 7), true); }); test('applies attribution to inner span', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 2, end: 7); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 2, end: 7); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), true); }); test('applies attribution to end of span', () { - final spans = AttributedSpans() - ..addAttribution(newAttribution: bold, start: 7, end: 16); + final spans = AttributedSpans()..addAttribution(newAttribution: bold, start: 7, end: 16); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 7, end: 16), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 7, end: 16), true); }); test('applies exotic span', () { final linkAttribution = _LinkAttribution( url: 'https://youtube.com/c/superdeclarative', ); - final spans = AttributedSpans() - ..addAttribution(newAttribution: linkAttribution, start: 2, end: 7); + final spans = AttributedSpans()..addAttribution(newAttribution: linkAttribution, start: 2, end: 7); - expect( - spans.hasAttributionsWithin( - attributions: {linkAttribution}, start: 2, end: 7), - true); + expect(spans.hasAttributionsWithin(attributions: {linkAttribution}, start: 2, end: 7), true); }); test('removes attribution from full span', () { final spans = AttributedSpans( attributions: [ - const SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - const SpanMarker( - attribution: bold, offset: 16, markerType: SpanMarkerType.end) + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 0, end: 16); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), - false); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), false); }); test('removes attribution from inner text span', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 7); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), - false); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 7), false); }); test('removes attribution from partial beginning span', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 4); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 5, end: 7), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 5, end: 7), true); }); test('removes attribution from partial inner span', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 4, end: 5); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 3), - true); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 6, end: 7), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 3), true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 6, end: 7), true); }); test('removes attribution from partial ending span', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 5, end: 7); - expect( - spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 4), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 2, end: 4), true); }); test('applies attribution when mixed span is toggled', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 8, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 16, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), true); }); test('removes attribution when contiguous span is toggled', () { final spans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 16, markerType: SpanMarkerType.end) + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), - false); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 16), false); }); }); @@ -427,10 +372,7 @@ void main() { end: 12, ); - expect( - spans - .hasAttributionsWithin(attributions: {bold}, start: 0, end: 12), - true); + expect(spans.hasAttributionsWithin(attributions: {bold}, start: 0, end: 12), true); }); }); @@ -444,10 +386,8 @@ void main() { test('single continuous attribution', () { final collapsedSpans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 16, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -461,14 +401,10 @@ void main() { test('single fractured attribution', () { final collapsedSpans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 10, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 10, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -492,18 +428,10 @@ void main() { test('multiple non-overlapping attributions', () { final collapsedSpans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker( - attribution: italics, - offset: 7, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: italics, - offset: 10, - markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), + SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.start), + SpanMarker(attribution: italics, offset: 10, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -527,18 +455,10 @@ void main() { test('multiple overlapping attributions', () { final collapsedSpans = AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 8, markerType: SpanMarkerType.end), - SpanMarker( - attribution: italics, - offset: 6, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: italics, - offset: 16, - markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.end), + SpanMarker(attribution: italics, offset: 6, markerType: SpanMarkerType.start), + SpanMarker(attribution: italics, offset: 16, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -589,15 +509,10 @@ class _ExpectedSpans { List _combinedSpans; void expectSpans(AttributedSpans spans) { - for (int characterIndex = 0; - characterIndex < _combinedSpans.length; - ++characterIndex) { - for (int attributionIndex = 0; - attributionIndex < _combinedSpans[characterIndex].length; - ++attributionIndex) { + for (int characterIndex = 0; characterIndex < _combinedSpans.length; ++characterIndex) { + for (int attributionIndex = 0; attributionIndex < _combinedSpans[characterIndex].length; ++attributionIndex) { // The attribution name is just a letter, like 'b', 'i', or 's'. - final attributionName = - _combinedSpans[characterIndex][attributionIndex]; + final attributionName = _combinedSpans[characterIndex][attributionIndex]; if (attributionName == '_') { continue; } @@ -614,14 +529,10 @@ class _ExpectedSpans { namedAttribution = strikethrough; break; default: - throw Exception( - 'Unknown span template character: $attributionName'); + throw Exception('Unknown span template character: $attributionName'); } - expect( - spans.hasAttributionAt(characterIndex, - attribution: namedAttribution), - true); + expect(spans.hasAttributionAt(characterIndex, attribution: namedAttribution), true); } } } @@ -644,10 +555,7 @@ class _LinkAttribution implements Attribution { @override bool operator ==(Object other) => - identical(this, other) || - other is _LinkAttribution && - runtimeType == other.runtimeType && - url == other.url; + identical(this, other) || other is _LinkAttribution && runtimeType == other.runtimeType && url == other.url; @override int get hashCode => url.hashCode; diff --git a/super_editor/test/src/infrastructure/attributed_text_test.dart b/super_editor/test/src/infrastructure/attributed_text_test.dart index 50bb6a4325..f5b47bc735 100644 --- a/super_editor/test/src/infrastructure/attributed_text_test.dart +++ b/super_editor/test/src/infrastructure/attributed_text_test.dart @@ -25,10 +25,8 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 9, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -44,10 +42,8 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 1, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 1, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), ], ), ); @@ -70,10 +66,8 @@ void main() { // Notice that the markers are provided in reverse order: // end then start. Order shouldn't matter within a single // position index. This test ensures that. - SpanMarker( - attribution: bold, offset: 1, markerType: SpanMarkerType.end), - SpanMarker( - attribution: bold, offset: 1, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), ], ), ); @@ -107,10 +101,8 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 7, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end), ], ), ); @@ -129,10 +121,8 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 9, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 9, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -141,10 +131,8 @@ void main() { text: 'k', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.end), ], ), )); @@ -168,10 +156,8 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 9, markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -184,8 +170,7 @@ void main() { expect(newText.text, 'aabcdefghij'); expect( - newText.hasAttributionsWithin( - attributions: {bold}, range: const TextRange(start: 0, end: 10)), + newText.hasAttributionsWithin(attributions: {bold}, range: const TextRange(start: 0, end: 10)), true, ); }); @@ -195,18 +180,10 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 4, markerType: SpanMarkerType.end), - SpanMarker( - attribution: italics, - offset: 5, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: italics, - offset: 9, - markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 4, markerType: SpanMarkerType.end), + SpanMarker(attribution: italics, offset: 5, markerType: SpanMarkerType.start), + SpanMarker(attribution: italics, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -227,18 +204,10 @@ void main() { text: 'abcdefghij', spans: AttributedSpans( attributions: const [ - SpanMarker( - attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker( - attribution: italics, - offset: 4, - markerType: SpanMarkerType.start), - SpanMarker( - attribution: bold, offset: 5, markerType: SpanMarkerType.end), - SpanMarker( - attribution: italics, - offset: 7, - markerType: SpanMarkerType.end), + SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + SpanMarker(attribution: italics, offset: 4, markerType: SpanMarkerType.start), + SpanMarker(attribution: bold, offset: 5, markerType: SpanMarkerType.end), + SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.end), ], ), ); diff --git a/super_editor/test/src/infrastructure/super_textfield_test.dart b/super_editor/test/src/infrastructure/super_textfield_test.dart index 4d15bb1bea..9377831935 100644 --- a/super_editor/test/src/infrastructure/super_textfield_test.dart +++ b/super_editor/test/src/infrastructure/super_textfield_test.dart @@ -38,8 +38,7 @@ void main() { } }); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -62,8 +61,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -85,8 +83,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -107,8 +104,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -149,8 +145,7 @@ void main() { } }); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -173,8 +168,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -196,8 +190,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -218,8 +211,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = - DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.copyTextWhenCmdCIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -260,8 +252,7 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -278,8 +269,7 @@ void main() { WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { // We have to run these expectations in the next frame // so that the async paste operation has time to complete. - expect(controller.text.text, - 'Pasted content: this is clipboard text'); + expect(controller.text.text, 'Pasted content: this is clipboard text'); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 38); }); @@ -292,8 +282,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -315,8 +304,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -338,8 +326,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -378,8 +365,7 @@ void main() { } }); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -396,8 +382,7 @@ void main() { WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { // We have to run these expectations in the next frame // so that the async paste operation has time to complete. - expect(controller.text.text, - 'Pasted content: this is clipboard text'); + expect(controller.text.text, 'Pasted content: this is clipboard text'); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 38); }); @@ -410,8 +395,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -433,8 +417,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -456,8 +439,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .pasteTextWhenCmdVIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.pasteTextWhenCmdVIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -486,8 +468,7 @@ void main() { selection: const TextSelection.collapsed(offset: 5), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -511,8 +492,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -534,8 +514,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -557,8 +536,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -585,8 +563,7 @@ void main() { selection: const TextSelection.collapsed(offset: 5), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -610,8 +587,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -633,8 +609,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -656,8 +631,7 @@ void main() { final controller = AttributedTextEditingController(); - final result = DefaultSuperTextFieldKeyboardHandlers - .selectAllTextFieldWhenCmdAIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.selectAllTextFieldWhenCmdAIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -678,19 +652,16 @@ void main() { group('move caret when arrow key is pressed', () { group('left arrow', () { - testWidgets('it does nothing at beginning of text blob', - (tester) async { + testWidgets('it does nothing at beginning of text blob', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), selection: const TextSelection.collapsed(offset: 0), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); // Move by character - final characterResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final characterResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -707,8 +678,7 @@ void main() { expect(controller.selection.extentOffset, 0); // Move by word - final wordResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final wordResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -726,8 +696,7 @@ void main() { expect(controller.selection.extentOffset, 0); // Move to end of line - final lineResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final lineResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -751,11 +720,9 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -782,8 +749,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -809,16 +775,8 @@ void main() { // We give a tiny bit of wiggle room on the value because when this test // is run on Windows and Linux CI, there is some kind of precision error // that results in a tiny positive number instead of zero. - expect( - selectableTextState - .getCharacterBox(const TextPosition(offset: 16)) - .top, - lessThan(0.1)); - expect( - selectableTextState - .getCharacterBox(const TextPosition(offset: 16)) - .top, - greaterThanOrEqualTo(0)); + expect(selectableTextState.getCharacterBox(const TextPosition(offset: 16)).top, lessThan(0.1)); + expect(selectableTextState.getCharacterBox(const TextPosition(offset: 16)).top, greaterThanOrEqualTo(0)); }); testWidgets('it expands left by character', (tester) async { @@ -827,11 +785,9 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -856,11 +812,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -884,11 +838,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -908,8 +860,7 @@ void main() { expect(controller.selection.extentOffset, 6); }); - testWidgets('Mac: cmd+left moves left to beginning of line', - (tester) async { + testWidgets('Mac: cmd+left moves left to beginning of line', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( @@ -917,11 +868,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -941,9 +890,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets( - 'Windows + Linux: control + left moves left to beginning of line', - (tester) async { + testWidgets('Windows + Linux: control + left moves left to beginning of line', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( @@ -951,11 +898,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -975,9 +920,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets( - 'Mac: cmd + shift + left expands left to beginning of line', - (tester) async { + testWidgets('Mac: cmd + shift + left expands left to beginning of line', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( @@ -985,11 +928,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1011,9 +952,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets( - 'Windows + Linux: control + shift + left expands left to beginning of line', - (tester) async { + testWidgets('Windows + Linux: control + shift + left expands left to beginning of line', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( @@ -1021,11 +960,9 @@ void main() { selection: const TextSelection.collapsed(offset: 10), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1047,8 +984,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it collapses downstream selection on left side', - (tester) async { + testWidgets('it collapses downstream selection on left side', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), selection: const TextSelection( @@ -1057,11 +993,9 @@ void main() { ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1078,8 +1012,7 @@ void main() { expect(controller.selection.extentOffset, 6); }); - testWidgets('it collapses upstream selection on left side', - (tester) async { + testWidgets('it collapses upstream selection on left side', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), selection: const TextSelection( @@ -1088,11 +1021,9 @@ void main() { ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1117,12 +1048,10 @@ void main() { selection: const TextSelection.collapsed(offset: 16), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); // Move by character - final characterResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final characterResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1139,8 +1068,7 @@ void main() { expect(controller.selection.extentOffset, 16); // Move by word - final wordResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final wordResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1158,8 +1086,7 @@ void main() { expect(controller.selection.extentOffset, 16); // Move to end of line - final lineResult = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final lineResult = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1183,11 +1110,9 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1214,8 +1139,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1234,11 +1158,7 @@ void main() { // We should have gone from line 1 to line 2. Make double sure by // checking that the bounding box for the character that's now selected // does not sit at the top of the text box. - expect( - selectableTextState - .getCharacterBox(const TextPosition(offset: 18)) - .top, - isNonZero); + expect(selectableTextState.getCharacterBox(const TextPosition(offset: 18)).top, isNonZero); }); testWidgets('it expands right by character', (tester) async { @@ -1247,11 +1167,9 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1276,11 +1194,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1304,11 +1220,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1328,8 +1242,7 @@ void main() { expect(controller.selection.extentOffset, 10); }); - testWidgets('Mac: cmd + right moves right to end of line', - (tester) async { + testWidgets('Mac: cmd + right moves right to end of line', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( @@ -1337,11 +1250,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1361,9 +1272,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets( - 'Windows + Linux: control + right moves right to end of line', - (tester) async { + testWidgets('Windows + Linux: control + right moves right to end of line', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( @@ -1371,11 +1280,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1395,8 +1302,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('Mac: cmd + shift + right expands right to end of line', - (tester) async { + testWidgets('Mac: cmd + shift + right expands right to end of line', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( @@ -1404,11 +1310,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1430,9 +1334,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets( - 'Windows + Linux: control + shift + right expands right to end of line', - (tester) async { + testWidgets('Windows + Linux: control + shift + right expands right to end of line', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( @@ -1440,11 +1342,9 @@ void main() { selection: const TextSelection.collapsed(offset: 6), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1466,8 +1366,7 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it collapses downstream selection on right side', - (tester) async { + testWidgets('it collapses downstream selection on right side', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), selection: const TextSelection( @@ -1476,11 +1375,9 @@ void main() { ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1497,8 +1394,7 @@ void main() { expect(controller.selection.extentOffset, 10); }); - testWidgets('it collapses upstream selection on right side', - (tester) async { + testWidgets('it collapses upstream selection on right side', (tester) async { final controller = AttributedTextEditingController( text: AttributedText(text: 'super text field'), selection: const TextSelection( @@ -1507,11 +1403,9 @@ void main() { ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1530,8 +1424,7 @@ void main() { }); group('up arrow', () { - testWidgets('it moves to start of text when in first line', - (tester) async { + testWidgets('it moves to start of text when in first line', (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( @@ -1541,8 +1434,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1569,8 +1461,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1597,8 +1488,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1617,8 +1507,7 @@ void main() { expect(controller.selection.baseOffset, 18); }); - testWidgets('it preserves horizontal position in previous line', - (tester) async { + testWidgets('it preserves horizontal position in previous line', (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( @@ -1628,8 +1517,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1648,8 +1536,7 @@ void main() { }); group('down arrow', () { - testWidgets('it moves to end of text when in last line', - (tester) async { + testWidgets('it moves to end of text when in last line', (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( @@ -1659,8 +1546,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1674,8 +1560,7 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); - expect( - controller.selection.extentOffset, _multilineLayoutText.length); + expect(controller.selection.extentOffset, _multilineLayoutText.length); }); testWidgets('it moves to next line', (tester) async { @@ -1688,8 +1573,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1716,8 +1600,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1736,8 +1619,7 @@ void main() { expect(controller.selection.baseOffset, 0); }); - testWidgets('it preserves horizontal position in next line', - (tester) async { + testWidgets('it preserves horizontal position in next line', (tester) async { // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( @@ -1747,8 +1629,7 @@ void main() { final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .moveUpDownLeftAndRightWithArrowKeys( + final result = DefaultSuperTextFieldKeyboardHandlers.moveUpDownLeftAndRightWithArrowKeys( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1769,23 +1650,20 @@ void main() { group('delete line before caret', () { group('Mac', () { - testWidgets( - 'cmd + backspace deletes partial line before caret (flowed multiline)', - (tester) async { + testWidgets('cmd + backspace deletes partial line before caret (flowed multiline)', (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 28), // midway through 2nd line + selection: const TextSelection.collapsed(offset: 28), // midway through 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1801,29 +1679,25 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, - 'this text is long be multiline in the available space'); + expect(controller.text.text, 'this text is long be multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets('cmd + backspace deletes entire line (flowed multiline)', - (tester) async { + testWidgets('cmd + backspace deletes entire line (flowed multiline)', (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 31, - affinity: TextAffinity.upstream), // end of 2nd line + selection: const TextSelection.collapsed(offset: 31, affinity: TextAffinity.upstream), // end of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1839,29 +1713,23 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, - 'this text is long multiline in the available space'); + expect(controller.text.text, 'this text is long multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets( - 'cmd + backspace deletes partial line before caret (explicit newlines)', - (tester) async { + testWidgets('cmd + backspace deletes partial line before caret (explicit newlines)', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: const TextSelection.collapsed( - offset: 23), // midway through 2nd line + text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed(offset: 23), // midway through 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1877,29 +1745,23 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 15); - expect( - controller.text.text, 'This is line 1\nline 2\nThis is line 3'); + expect(controller.text.text, 'This is line 1\nline 2\nThis is line 3'); Platform.setTestInstance(null); }); - testWidgets('cmd + backspace deletes entire line (explicit newlines)', - (tester) async { + testWidgets('cmd + backspace deletes entire line (explicit newlines)', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: const TextSelection.collapsed( - offset: 29, - affinity: TextAffinity.upstream), // end of 2nd line + text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed(offset: 29, affinity: TextAffinity.upstream), // end of 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1920,25 +1782,21 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is expanded', - (tester) async { + testWidgets('it does nothing when selection is expanded', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: - 'This is some text that doesn\'t matter for this test.'), + text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), selection: const TextSelection( baseOffset: 0, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1956,22 +1814,18 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection extent is < 0', - (tester) async { + testWidgets('it does nothing when selection extent is < 0', (tester) async { Platform.setTestInstance(MacPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: - 'This is some text that doesn\'t matter for this test.'), + text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), selection: const TextSelection.collapsed(offset: -1), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -1989,22 +1843,20 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is at start of line', - (tester) async { + testWidgets('it does nothing when selection is at start of line', (tester) async { Platform.setTestInstance(MacPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 18), // start of 2nd line + selection: const TextSelection.collapsed(offset: 18), // start of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2024,23 +1876,20 @@ void main() { }); group('Windows + Linux', () { - testWidgets( - 'control + backspace deletes partial line before caret (flowed multiline)', - (tester) async { + testWidgets('control + backspace deletes partial line before caret (flowed multiline)', (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 28), // midway through 2nd line + selection: const TextSelection.collapsed(offset: 28), // midway through 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2056,30 +1905,25 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, - 'this text is long be multiline in the available space'); + expect(controller.text.text, 'this text is long be multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets( - 'control + backspace deletes entire line (flowed multiline)', - (tester) async { + testWidgets('control + backspace deletes entire line (flowed multiline)', (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 31, - affinity: TextAffinity.upstream), // end of 2nd line + selection: const TextSelection.collapsed(offset: 31, affinity: TextAffinity.upstream), // end of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2095,29 +1939,23 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 18); - expect(controller.text.text, - 'this text is long multiline in the available space'); + expect(controller.text.text, 'this text is long multiline in the available space'); Platform.setTestInstance(null); }); - testWidgets( - 'control + backspace deletes partial line before caret (explicit newlines)', - (tester) async { + testWidgets('control + backspace deletes partial line before caret (explicit newlines)', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: const TextSelection.collapsed( - offset: 23), // midway through 2nd line + text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed(offset: 23), // midway through 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2133,30 +1971,23 @@ void main() { expect(result, TextFieldKeyboardHandlerResult.handled); expect(controller.selection.isCollapsed, true); expect(controller.selection.extentOffset, 15); - expect( - controller.text.text, 'This is line 1\nline 2\nThis is line 3'); + expect(controller.text.text, 'This is line 1\nline 2\nThis is line 3'); Platform.setTestInstance(null); }); - testWidgets( - 'control + backspace deletes entire line (explicit newlines)', - (tester) async { + testWidgets('control + backspace deletes entire line (explicit newlines)', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: 'This is line 1\nThis is line 2\nThis is line 3'), - selection: const TextSelection.collapsed( - offset: 29, - affinity: TextAffinity.upstream), // end of 2nd line + text: AttributedText(text: 'This is line 1\nThis is line 2\nThis is line 3'), + selection: const TextSelection.collapsed(offset: 29, affinity: TextAffinity.upstream), // end of 2nd line ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2177,25 +2008,21 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is expanded', - (tester) async { + testWidgets('it does nothing when selection is expanded', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: - 'This is some text that doesn\'t matter for this test.'), + text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), selection: const TextSelection( baseOffset: 0, extentOffset: 10, ), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2213,22 +2040,18 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection extent is < 0', - (tester) async { + testWidgets('it does nothing when selection extent is < 0', (tester) async { Platform.setTestInstance(WindowsPlatform()); final controller = AttributedTextEditingController( - text: AttributedText( - text: - 'This is some text that doesn\'t matter for this test.'), + text: AttributedText(text: 'This is some text that doesn\'t matter for this test.'), selection: const TextSelection.collapsed(offset: -1), ); - final selectableTextState = await _pumpAndReturnSelectableText( - tester, controller.text.text); + final selectableTextState = await _pumpAndReturnSelectableText(tester, controller.text.text); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2246,22 +2069,20 @@ void main() { Platform.setTestInstance(null); }); - testWidgets('it does nothing when selection is at start of line', - (tester) async { + testWidgets('it does nothing when selection is at start of line', (tester) async { Platform.setTestInstance(WindowsPlatform()); // Note: this test depends on a multi-line text layout, therefore // the layout width and the text content must be precise. final controller = AttributedTextEditingController( text: AttributedText(text: _multilineLayoutText), - selection: const TextSelection.collapsed( - offset: 18), // start of 2nd line + selection: const TextSelection.collapsed(offset: 18), // start of 2nd line ); final selectableTextState = await _pumpMultilineLayout(tester); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( + final result = + DefaultSuperTextFieldKeyboardHandlers.deleteTextOnLineBeforeCaretWhenShortcutKeyAndBackspaceIsPressed( controller: controller, selectableTextState: selectableTextState, keyEvent: const FakeRawKeyEvent( @@ -2288,8 +2109,7 @@ void main() { selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2312,8 +2132,7 @@ void main() { selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2336,8 +2155,7 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2363,8 +2181,7 @@ void main() { ), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2389,8 +2206,7 @@ void main() { selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2413,8 +2229,7 @@ void main() { selection: const TextSelection.collapsed(offset: 17), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2437,8 +2252,7 @@ void main() { selection: const TextSelection.collapsed(offset: 2), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2464,8 +2278,7 @@ void main() { ), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .deleteTextWhenBackspaceOrDeleteIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.deleteTextWhenBackspaceOrDeleteIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2490,8 +2303,7 @@ void main() { selection: const TextSelection.collapsed(offset: 8), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2514,8 +2326,7 @@ void main() { selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2538,8 +2349,7 @@ void main() { selection: const TextSelection.collapsed(offset: 17), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertNewlineWhenEnterIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertNewlineWhenEnterIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2564,8 +2374,7 @@ void main() { selection: const TextSelection.collapsed(offset: 0), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2588,8 +2397,7 @@ void main() { selection: const TextSelection.collapsed(offset: 3), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2612,8 +2420,7 @@ void main() { selection: const TextSelection.collapsed(offset: 3), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2636,8 +2443,7 @@ void main() { selection: const TextSelection(baseOffset: 3, extentOffset: 10), ); - final result = DefaultSuperTextFieldKeyboardHandlers - .insertCharacterWhenKeyIsPressed( + final result = DefaultSuperTextFieldKeyboardHandlers.insertCharacterWhenKeyIsPressed( controller: controller, keyEvent: const FakeRawKeyEvent( data: FakeRawKeyEventData( @@ -2658,8 +2464,7 @@ void main() { }); } -const _multilineLayoutText = - 'this text is long enough to be multiline in the available space'; +const _multilineLayoutText = 'this text is long enough to be multiline in the available space'; // Based on experiments, the text is laid out as follows: // @@ -2704,8 +2509,7 @@ Future _pumpAndReturnSelectableText( style: const TextStyle(), ); - final decoratedText = - decorator == null ? selectableText : decorator(selectableText); + final decoratedText = decorator == null ? selectableText : decorator(selectableText); await tester.pumpWidget( MaterialApp( From 4809499c40c099d4f36d3da03ecbd37602b31117 Mon Sep 17 00:00:00 2001 From: Brett Morgan Date: Mon, 7 Jun 2021 09:38:43 +1000 Subject: [PATCH 3/3] const lists -> list of consts --- super_editor/example/pubspec.lock | 2 +- .../lib/src/core/document_layout.dart | 29 +++----- super_editor/pubspec.lock | 2 +- .../test/serialization/markdown_test.dart | 10 +-- .../infrastructure/attributed_spans_test.dart | 72 +++++++++---------- .../infrastructure/attributed_text_test.dart | 62 ++++++++-------- 6 files changed, 83 insertions(+), 94 deletions(-) diff --git a/super_editor/example/pubspec.lock b/super_editor/example/pubspec.lock index cafd8d7788..0793b42fda 100644 --- a/super_editor/example/pubspec.lock +++ b/super_editor/example/pubspec.lock @@ -251,7 +251,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: diff --git a/super_editor/lib/src/core/document_layout.dart b/super_editor/lib/src/core/document_layout.dart index 9efac1cb9a..201a7c984e 100644 --- a/super_editor/lib/src/core/document_layout.dart +++ b/super_editor/lib/src/core/document_layout.dart @@ -44,14 +44,12 @@ abstract class DocumentLayout { /// Returns a [Rect] that bounds the content selected between /// [basePosition] and [extentPosition]. - Rect? getRectForSelection( - DocumentPosition basePosition, DocumentPosition extentPosition); + Rect? getRectForSelection(DocumentPosition basePosition, DocumentPosition extentPosition); /// Returns a [DocumentSelection] that begins near [baseOffset] and extends /// to [extentOffset], or [null] if no document content sits between the /// provided points. - DocumentSelection? getDocumentSelectionInRegion( - Offset baseOffset, Offset extentOffset); + DocumentSelection? getDocumentSelectionInRegion(Offset baseOffset, Offset extentOffset); /// Returns the [MouseCursor] that's desired by the component at [documentOffset], or /// [null] if the document has no preference for the [MouseCursor] at the given @@ -64,13 +62,11 @@ abstract class DocumentLayout { /// Converts [ancestorOffset] from the [ancestor]'s coordinate space to the /// same location on the screen within this [DocumentLayout]'s coordinate space. - Offset getDocumentOffsetFromAncestorOffset( - Offset ancestorOffset, RenderObject ancestor); + Offset getDocumentOffsetFromAncestorOffset(Offset ancestorOffset, RenderObject ancestor); /// Converts [documentOffset] from this [DocumentLayout]'s coordinate space /// to the same location on the screen within the [ancestor]'s coordinate space. - Offset getAncestorOffsetFromDocumentOffset( - Offset documentOffset, RenderObject ancestor); + Offset getAncestorOffsetFromDocumentOffset(Offset documentOffset, RenderObject ancestor); } /// Contract for all widgets that operate as document components @@ -117,8 +113,7 @@ mixin DocumentComponent on State { /// /// See [Document] for more information about [DocumentNode]s and /// node positions. - Rect getRectForSelection( - NodePosition baseNodePosition, NodePosition extentNodePosition); + Rect getRectForSelection(NodePosition baseNodePosition, NodePosition extentNodePosition); /// Returns the node position that represents the "beginning" of /// the content within this component, such as the first character @@ -150,8 +145,7 @@ mixin DocumentComponent on State { /// Returns [null] if there is nowhere to move left within this /// component, such as when the [currentPosition] is the first /// character within a paragraph. - NodePosition? movePositionLeft(NodePosition currentPosition, - [Set movementModifiers]); + NodePosition? movePositionLeft(NodePosition currentPosition, [Set movementModifiers]); /// Returns a new position within this component's node that /// corresponds to the [currentPosition] moved right one unit, @@ -167,8 +161,7 @@ mixin DocumentComponent on State { /// Returns null if there is nowhere to move right within this /// component, such as when the [currentPosition] refers to the /// last character in a paragraph. - NodePosition? movePositionRight(NodePosition currentPosition, - [Set movementModifiers]); + NodePosition? movePositionRight(NodePosition currentPosition, [Set movementModifiers]); /// Returns a new position within this component's node that /// corresponds to the [currentPosition] moved up one unit, @@ -216,8 +209,7 @@ mixin DocumentComponent on State { /// /// The selection type depends on the type of [DocumentNode] that this /// component displays. - NodeSelection? getSelectionInRange( - Offset localBaseOffset, Offset localExtentOffset); + NodeSelection? getSelectionInRange(Offset localBaseOffset, Offset localExtentOffset); /// Returns a [NodeSelection] within this component's [DocumentNode] that /// is collapsed at the given [nodePosition] @@ -282,10 +274,7 @@ class MovementModifier { @override bool operator ==(Object other) => - identical(this, other) || - other is MovementModifier && - runtimeType == other.runtimeType && - id == other.id; + identical(this, other) || other is MovementModifier && runtimeType == other.runtimeType && id == other.id; @override int get hashCode => id.hashCode; diff --git a/super_editor/pubspec.lock b/super_editor/pubspec.lock index 02a5112037..fc320fb00e 100644 --- a/super_editor/pubspec.lock +++ b/super_editor/pubspec.lock @@ -274,7 +274,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" stack_trace: dependency: transitive description: diff --git a/super_editor/test/serialization/markdown_test.dart b/super_editor/test/serialization/markdown_test.dart index 1a37d7118a..10704e893a 100644 --- a/super_editor/test/serialization/markdown_test.dart +++ b/super_editor/test/serialization/markdown_test.dart @@ -162,11 +162,11 @@ This is some code text: AttributedText( text: 'This is a paragraph.', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), - SpanMarker(attribution: codeAttribution, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: codeAttribution, offset: 8, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: boldAttribution, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: boldAttribution, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker(attribution: codeAttribution, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: codeAttribution, offset: 8, markerType: SpanMarkerType.end), ], ), ), diff --git a/super_editor/test/src/infrastructure/attributed_spans_test.dart b/super_editor/test/src/infrastructure/attributed_spans_test.dart index ad92b53c61..54eed0c3e1 100644 --- a/super_editor/test/src/infrastructure/attributed_spans_test.dart +++ b/super_editor/test/src/infrastructure/attributed_spans_test.dart @@ -206,9 +206,9 @@ void main() { test('removes attribution from inner text span', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 7); @@ -217,9 +217,9 @@ void main() { test('removes attribution from partial beginning span', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 2, end: 4); @@ -228,9 +228,9 @@ void main() { test('removes attribution from partial inner span', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 4, end: 5); @@ -240,9 +240,9 @@ void main() { test('removes attribution from partial ending span', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end) ], )..removeAttribution(attributionToRemove: bold, start: 5, end: 7); @@ -251,9 +251,9 @@ void main() { test('applies attribution when mixed span is toggled', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); @@ -262,9 +262,9 @@ void main() { test('removes attribution when contiguous span is toggled', () { final spans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end) ], )..toggleAttribution(attribution: bold, start: 0, end: 16); @@ -385,9 +385,9 @@ void main() { test('single continuous attribution', () { final collapsedSpans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 16, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -400,11 +400,11 @@ void main() { test('single fractured attribution', () { final collapsedSpans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 10, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 10, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -427,11 +427,11 @@ void main() { test('multiple non-overlapping attributions', () { final collapsedSpans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 10, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 3, markerType: SpanMarkerType.end), + const SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.start), + const SpanMarker(attribution: italics, offset: 10, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); @@ -454,11 +454,11 @@ void main() { test('multiple overlapping attributions', () { final collapsedSpans = AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 6, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 16, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 8, markerType: SpanMarkerType.end), + const SpanMarker(attribution: italics, offset: 6, markerType: SpanMarkerType.start), + const SpanMarker(attribution: italics, offset: 16, markerType: SpanMarkerType.end), ], ).collapseSpans(contentLength: 17); diff --git a/super_editor/test/src/infrastructure/attributed_text_test.dart b/super_editor/test/src/infrastructure/attributed_text_test.dart index f5b47bc735..b3f3d1f7a0 100644 --- a/super_editor/test/src/infrastructure/attributed_text_test.dart +++ b/super_editor/test/src/infrastructure/attributed_text_test.dart @@ -24,9 +24,9 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -41,9 +41,9 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), ], ), ); @@ -62,12 +62,12 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ + attributions: [ // Notice that the markers are provided in reverse order: // end then start. Order shouldn't matter within a single // position index. This test ensures that. - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), - SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.end), + const SpanMarker(attribution: bold, offset: 1, markerType: SpanMarkerType.start), ], ), ); @@ -100,9 +100,9 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 7, markerType: SpanMarkerType.end), ], ), ); @@ -120,9 +120,9 @@ void main() { final initialText = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -130,9 +130,9 @@ void main() { final newText = initialText.copyAndAppend(AttributedText( text: 'k', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.end), ], ), )); @@ -155,9 +155,9 @@ void main() { final initialText = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -179,11 +179,11 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 4, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 5, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 9, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 0, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 4, markerType: SpanMarkerType.end), + const SpanMarker(attribution: italics, offset: 5, markerType: SpanMarkerType.start), + const SpanMarker(attribution: italics, offset: 9, markerType: SpanMarkerType.end), ], ), ); @@ -203,11 +203,11 @@ void main() { final text = AttributedText( text: 'abcdefghij', spans: AttributedSpans( - attributions: const [ - SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), - SpanMarker(attribution: italics, offset: 4, markerType: SpanMarkerType.start), - SpanMarker(attribution: bold, offset: 5, markerType: SpanMarkerType.end), - SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.end), + attributions: [ + const SpanMarker(attribution: bold, offset: 2, markerType: SpanMarkerType.start), + const SpanMarker(attribution: italics, offset: 4, markerType: SpanMarkerType.start), + const SpanMarker(attribution: bold, offset: 5, markerType: SpanMarkerType.end), + const SpanMarker(attribution: italics, offset: 7, markerType: SpanMarkerType.end), ], ), );