diff --git a/lib/src/core/document/text_delta.dart b/lib/src/core/document/text_delta.dart index 9aa66d962..7a80376f1 100644 --- a/lib/src/core/document/text_delta.dart +++ b/lib/src/core/document/text_delta.dart @@ -243,7 +243,7 @@ class Delta extends Iterable { /// Delete operations have a Number `delete` key defined representing the number of characters to delete. void delete(int length) => add(TextDelete(length: length)); - /// The length of the string fo the [Delta]. + /// The length of the string of the [Delta]. @override int get length { return _operations.fold( diff --git a/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart b/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart index 62622625f..aafae888c 100644 --- a/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart +++ b/lib/src/service/internal_key_event_handlers/arrow_keys_handler.dart @@ -39,7 +39,7 @@ ShortcutEventHandler cursorUpSelect = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - final end = _goUp(editorState); + final end = _moveVertical(editorState); if (end == null) { return KeyEventResult.ignored; } @@ -55,7 +55,7 @@ ShortcutEventHandler cursorDownSelect = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - final end = _goDown(editorState); + final end = _moveVertical(editorState, upwards: false); if (end == null) { return KeyEventResult.ignored; } @@ -191,6 +191,7 @@ ShortcutEventHandler cursorBeginSelect = (editorState, event) { if (position != null) { end = position; } + editorState.service.selectionService.updateSelection( selection.copyWith(start: start, end: end), ); @@ -223,10 +224,12 @@ ShortcutEventHandler cursorUp = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - final upPosition = _goUp(editorState); + + final upPosition = _moveVertical(editorState); editorState.updateCursorSelection( upPosition == null ? null : Selection.collapsed(upPosition), ); + return KeyEventResult.handled; }; @@ -237,10 +240,12 @@ ShortcutEventHandler cursorDown = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - final downPosition = _goDown(editorState); + + final downPosition = _moveVertical(editorState, upwards: false); editorState.updateCursorSelection( downPosition == null ? null : Selection.collapsed(downPosition), ); + return KeyEventResult.handled; }; @@ -251,18 +256,15 @@ ShortcutEventHandler cursorLeft = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - if (selection.isCollapsed) { - final leftPosition = selection.start.goLeft(editorState); - if (leftPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(leftPosition), - ); - } - } else { - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.start), - ); - } + + Position newPosition = selection.isCollapsed + ? selection.start.goLeft(editorState) ?? selection.start + : selection.start; + + editorState.service.selectionService.updateSelection( + Selection.collapsed(newPosition), + ); + return KeyEventResult.handled; }; @@ -273,18 +275,15 @@ ShortcutEventHandler cursorRight = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } - if (selection.isCollapsed) { - final rightPosition = selection.start.goRight(editorState); - if (rightPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(rightPosition), - ); - } - } else { - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.end), - ); - } + + final newPosition = selection.isCollapsed + ? selection.start.goRight(editorState) ?? selection.end + : selection.end; + + editorState.service.selectionService.updateSelection( + Selection.collapsed(newPosition), + ); + return KeyEventResult.handled; }; @@ -294,14 +293,17 @@ ShortcutEventHandler cursorLeftWordSelect = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } + final end = selection.end.goLeft(editorState, selectionRange: _SelectionRange.word); if (end == null) { return KeyEventResult.ignored; } + editorState.service.selectionService.updateSelection( selection.copyWith(end: end), ); + return KeyEventResult.handled; }; @@ -314,21 +316,13 @@ ShortcutEventHandler cursorLeftWordMove = (editorState, event) { return KeyEventResult.ignored; } - if (selection.isCollapsed) { - final leftPosition = selection.start - .goLeft(editorState, selectionRange: _SelectionRange.word); - if (leftPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(leftPosition), - ); - - return KeyEventResult.handled; - } + final newPosition = selection.start + .goLeft(editorState, selectionRange: _SelectionRange.word) ?? + selection.start; - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.start), - ); - } + editorState.service.selectionService.updateSelection( + Selection.collapsed(newPosition), + ); return KeyEventResult.handled; }; @@ -342,21 +336,13 @@ ShortcutEventHandler cursorRightWordMove = (editorState, event) { return KeyEventResult.ignored; } - if (selection.isCollapsed) { - final rightPosition = selection.start - .goRight(editorState, selectionRange: _SelectionRange.word); - if (rightPosition != null) { - editorState.service.selectionService.updateSelection( - Selection.collapsed(rightPosition), - ); - - return KeyEventResult.handled; - } + final newPosition = selection.start + .goRight(editorState, selectionRange: _SelectionRange.word) ?? + selection.end; - editorState.service.selectionService.updateSelection( - Selection.collapsed(selection.end), - ); - } + editorState.service.selectionService.updateSelection( + Selection.collapsed(newPosition), + ); return KeyEventResult.handled; }; @@ -367,14 +353,17 @@ ShortcutEventHandler cursorRightWordSelect = (editorState, event) { if (nodes.isEmpty || selection == null) { return KeyEventResult.ignored; } + final end = selection.end.goRight(editorState, selectionRange: _SelectionRange.word); if (end == null) { return KeyEventResult.ignored; } + editorState.service.selectionService.updateSelection( selection.copyWith(end: end), ); + return KeyEventResult.handled; }; @@ -405,6 +394,30 @@ ShortcutEventHandler cursorLeftWordDelete = (editorState, event) { return KeyEventResult.handled; }; +ShortcutEventHandler cursorLeftSentenceDelete = (editorState, event) { + final nodes = editorState.service.selectionService.currentSelectedNodes; + final selection = editorState.service.selectionService.currentSelection.value; + if (nodes.isEmpty || selection == null) { + return KeyEventResult.ignored; + } + + if (nodes.length == 1 && nodes.first is TextNode) { + final textNode = nodes.first as TextNode; + if (textNode.toPlainText().isEmpty) { + return KeyEventResult.ignored; + } + } + + if (selection.isCollapsed) { + final deleteTransaction = editorState.transaction; + deleteTransaction.deleteText( + nodes.first as TextNode, 0, selection.end.offset); + editorState.apply(deleteTransaction, withUpdateCursor: true); + } + + return KeyEventResult.handled; +}; + enum _SelectionRange { character, word, @@ -435,9 +448,9 @@ extension on Position { path: path, offset: node.delta.prevRunePosition(offset), ); - } else { - return Position(path: path, offset: offset); } + + return Position(path: path, offset: offset); case _SelectionRange.word: if (node is TextNode) { final result = node.selectable?.getWordBoundaryInPosition( @@ -449,11 +462,10 @@ extension on Position { if (result != null) { return result.start; } - } else { - return Position(path: path, offset: offset); } + + return Position(path: path, offset: offset); } - return null; } Position? goRight( @@ -464,14 +476,12 @@ extension on Position { if (node == null) { return null; } + final end = node.selectable?.end(); if (end != null && offset >= end.offset) { - final nextStart = node.next?.selectable?.start(); - if (nextStart != null) { - return nextStart; - } - return null; + return node.next?.selectable?.start(); } + switch (selectionRange) { case _SelectionRange.character: if (node is TextNode) { @@ -479,61 +489,48 @@ extension on Position { path: path, offset: node.delta.nextRunePosition(offset), ); - } else { - return Position(path: path, offset: offset); } + + return Position(path: path, offset: offset); case _SelectionRange.word: if (node is TextNode) { final result = node.selectable?.getWordBoundaryInPosition(this); if (result != null) { return result.end; } - } else { - return Position(path: path, offset: offset); } + + return Position(path: path, offset: offset); } - return null; } } -Position? _goUp(EditorState editorState) { +Position? _moveVertical( + EditorState editorState, { + bool upwards = true, +}) { final selection = editorState.service.selectionService.currentSelection.value; final rects = editorState.service.selectionService.selectionRects; if (rects.isEmpty || selection == null) { return null; } - Offset offset; - if (selection.isBackward) { - final rect = rects.reduce( - (current, next) => current.bottom >= next.bottom ? current : next, - ); - offset = rect.topRight.translate(0, -rect.height); - } else { - final rect = rects.reduce( - (current, next) => current.top <= next.top ? current : next, - ); - offset = rect.topLeft.translate(0, -rect.height); - } - return editorState.service.selectionService.getPositionInOffset(offset); -} -Position? _goDown(EditorState editorState) { - final selection = editorState.service.selectionService.currentSelection.value; - final rects = editorState.service.selectionService.selectionRects; - if (rects.isEmpty || selection == null) { - return null; - } Offset offset; if (selection.isBackward) { final rect = rects.reduce( (current, next) => current.bottom >= next.bottom ? current : next, ); - offset = rect.bottomRight.translate(0, rect.height); + offset = upwards + ? rect.topRight.translate(0, -rect.height) + : rect.bottomRight.translate(0, rect.height); } else { final rect = rects.reduce( (current, next) => current.top <= next.top ? current : next, ); - offset = rect.bottomLeft.translate(0, rect.height); + offset = upwards + ? rect.topLeft.translate(0, -rect.height) + : rect.bottomLeft.translate(0, rect.height); } + return editorState.service.selectionService.getPositionInOffset(offset); } diff --git a/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/lib/src/service/shortcut_event/built_in_shortcut_events.dart index 6c253134d..b9f52a19a 100644 --- a/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -63,11 +63,18 @@ List builtInShortcutEvents = [ ), ShortcutEvent( key: 'Cursor word delete', - command: 'meta+backspace', + command: 'alt+backspace', windowsCommand: 'ctrl+backspace', linuxCommand: 'ctrl+backspace', handler: cursorLeftWordDelete, ), + ShortcutEvent( + key: 'Cursor sentence delete', + command: 'meta+backspace', + windowsCommand: 'ctrl+alt+backspace', + linuxCommand: 'ctrl+alt+backspace', + handler: cursorLeftSentenceDelete, + ), ShortcutEvent( key: 'Cursor left select', command: 'shift+arrow left', diff --git a/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart b/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart index e1bcf22cd..86619b156 100644 --- a/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart +++ b/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart @@ -49,10 +49,72 @@ void main() async { }); }); - testWidgets( - 'Presses alt + arrow left/right key, move the cursor one word left/right', + testWidgets('Cursor up/down', (tester) async { + final editor = tester.editor + ..insertTextNode("Welcome") + ..insertTextNode("Welcome to AppFlowy"); + await editor.startTesting(); + + await editor.updateSelection(Selection.single(path: [1], startOffset: 19)); + + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowUp); + + expect( + editor.documentSelection, + Selection.single(path: [0], startOffset: 7), + ); + + await editor.pressLogicKey(key: LogicalKeyboardKey.arrowDown); + + expect( + editor.documentSelection, + Selection.single(path: [1], startOffset: 7), + ); + }); + + testWidgets('Cursor top/bottom select', (tester) async { + const text = 'Welcome to Appflowy'; + final editor = tester.editor + ..insertTextNode(text) + ..insertTextNode(text) + ..insertTextNode(text); + await editor.startTesting(); + + Future select(bool isTop) async { + await editor.pressLogicKey( + key: isTop ? LogicalKeyboardKey.arrowUp : LogicalKeyboardKey.arrowDown, + isMetaPressed: Platform.isMacOS, + isControlPressed: !Platform.isMacOS, + isShiftPressed: true, + ); + } + + await editor.updateSelection(Selection.single(path: [1], startOffset: 7)); + + await select(true); + + expect( + editor.documentSelection, + Selection( + start: Position(path: [1], offset: 7), + end: Position(path: [0]), + ), + ); + + await select(false); + + expect( + editor.documentSelection, + Selection( + start: Position(path: [1], offset: 7), + end: Position(path: [2], offset: 19), + ), + ); + }); + + testWidgets('Presses alt + arrow right key, move the cursor one word right', (tester) async { - const text = 'Welcome to Appflowy 😁'; + const text = 'Welcome to Appflowy'; final editor = tester.editor ..insertTextNode(text) ..insertTextNode(text); @@ -92,6 +154,71 @@ void main() async { ), ); + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowRight, + ); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowRight, + isAltPressed: true, + ); + + expect( + editor.documentSelection, + Selection.single( + path: [0], + startOffset: 19, + ), + ); + + /// If the node does not exist, goRight will return + /// null, allowing us to test the edgecase of + /// move right word + editor.document.delete([0]); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowRight, + isAltPressed: true, + ); + + expect( + editor.documentSelection, + Selection.single( + path: [0], + startOffset: 19, + ), + ); + }); + + testWidgets('Presses alt + arrow left key, move the cursor one word left', + (tester) async { + const text = 'Welcome to Appflowy'; + final editor = tester.editor + ..insertTextNode(text) + ..insertTextNode(text); + await editor.startTesting(); + + await editor.updateSelection( + Selection.single(path: [0], startOffset: 19), + ); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowLeft, + isAltPressed: true, + ); + + expect( + editor.documentSelection, + Selection.single( + path: [0], + startOffset: 11, + ), + ); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowLeft, + ); + await editor.pressLogicKey( key: LogicalKeyboardKey.arrowLeft, isAltPressed: true, @@ -104,6 +231,41 @@ void main() async { startOffset: 8, ), ); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowLeft, + ); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowLeft, + isAltPressed: true, + ); + + expect( + editor.documentSelection, + Selection.single( + path: [0], + startOffset: 0, + ), + ); + + /// If the node does not exist, goRight will return + /// null, allowing us to test the edgecase of + /// move left word + editor.document.delete([0]); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.arrowLeft, + isAltPressed: true, + ); + + expect( + editor.documentSelection, + Selection.single( + path: [0], + startOffset: 0, + ), + ); }); testWidgets( @@ -541,7 +703,7 @@ void main() async { } else { await editor.pressLogicKey( key: LogicalKeyboardKey.backspace, - isMetaPressed: true, + isAltPressed: true, ); } @@ -562,7 +724,7 @@ void main() async { } else { await editor.pressLogicKey( key: LogicalKeyboardKey.backspace, - isMetaPressed: true, + isAltPressed: true, ); } @@ -584,7 +746,7 @@ void main() async { } else { await editor.pressLogicKey( key: LogicalKeyboardKey.backspace, - isMetaPressed: true, + isAltPressed: true, ); } } @@ -613,7 +775,7 @@ void main() async { } else { await editor.pressLogicKey( key: LogicalKeyboardKey.backspace, - isMetaPressed: true, + isAltPressed: true, ); } @@ -638,7 +800,7 @@ void main() async { } else { await editor.pressLogicKey( key: LogicalKeyboardKey.backspace, - isMetaPressed: true, + isAltPressed: true, ); } diff --git a/test/service/internal_key_event_handlers/backspace_handler_test.dart b/test/service/internal_key_event_handlers/backspace_handler_test.dart index b299c18aa..0c924945b 100644 --- a/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ b/test/service/internal_key_event_handlers/backspace_handler_test.dart @@ -59,6 +59,7 @@ void main() async { (tester) async { await _deleteTextByBackspace(tester, true); }); + testWidgets( 'Presses backspace key in non-empty document and selection is forward', (tester) async { @@ -85,6 +86,7 @@ void main() async { (tester) async { await _deleteTextByDelete(tester, true); }); + testWidgets( 'Presses delete key in non-empty document and selection is forward', (tester) async { @@ -135,14 +137,17 @@ void main() async { (tester) async { await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.checkbox); }); + testWidgets('Presses backspace key in styled text (bulletedList)', (tester) async { await _deleteStyledTextByBackspace( tester, BuiltInAttributeKey.bulletedList); }); + testWidgets('Presses backspace key in styled text (heading)', (tester) async { await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.heading); }); + testWidgets('Presses backspace key in styled text (quote)', (tester) async { await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.quote); }); @@ -161,13 +166,16 @@ void main() async { testWidgets('Presses delete key in styled text (checkbox)', (tester) async { await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.checkbox); }); + testWidgets('Presses delete key in styled text (bulletedList)', (tester) async { await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.bulletedList); }); + testWidgets('Presses delete key in styled text (heading)', (tester) async { await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.heading); }); + testWidgets('Presses delete key in styled text (quote)', (tester) async { await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.quote); }); @@ -251,7 +259,7 @@ void main() async { await editor.insertText(textNode, '#', 0); await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( - (editor.nodeAtPath([0]) as TextNode).attributes.heading, + textNode.attributes.heading, BuiltInAttributeKey.h1, ); @@ -264,7 +272,7 @@ void main() async { await editor.insertText(textNode, '#', 0); await editor.pressLogicKey(key: LogicalKeyboardKey.space); expect( - (editor.nodeAtPath([0]) as TextNode).attributes.heading, + textNode.attributes.heading, BuiltInAttributeKey.h1, ); }); @@ -298,16 +306,19 @@ void main() async { ); await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([0, 0, 0])?.subtype, null); + await editor.updateSelection( Selection.single(path: [0, 0, 0], startOffset: 0), ); await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([0, 1]) != null, true); + await editor.updateSelection( Selection.single(path: [0, 1], startOffset: 0), ); await editor.pressLogicKey(key: LogicalKeyboardKey.backspace); expect(editor.nodeAtPath([1]) != null, true); + await editor.updateSelection( Selection.single(path: [1], startOffset: 0), ); @@ -361,14 +372,17 @@ void main() async { editor.nodeAtPath([0, 1])!.subtype != BuiltInAttributeKey.bulletedList, true, ); + expect( editor.nodeAtPath([0, 1, 0])!.subtype, BuiltInAttributeKey.bulletedList, ); + expect( editor.nodeAtPath([0, 1, 1])!.subtype, BuiltInAttributeKey.bulletedList, ); + expect(find.byType(FlowyRichText), findsNWidgets(5)); // Before @@ -387,14 +401,17 @@ void main() async { editor.nodeAtPath([0, 0])!.subtype == BuiltInAttributeKey.bulletedList, true, ); + expect( (editor.nodeAtPath([0, 0]) as TextNode).toPlainText() == text * 2, true, ); + expect( editor.nodeAtPath([0, 1])!.subtype == BuiltInAttributeKey.bulletedList, true, ); + expect( editor.nodeAtPath([0, 2])!.subtype == BuiltInAttributeKey.bulletedList, true, diff --git a/test/service/shortcut_event/shortcut_event_test.dart b/test/service/shortcut_event/shortcut_event_test.dart index a8a827140..026c3e8c3 100644 --- a/test/service/shortcut_event/shortcut_event_test.dart +++ b/test/service/shortcut_event/shortcut_event_test.dart @@ -21,6 +21,7 @@ void main() async { return KeyEventResult.handled; }, ); + shortcutEvent.updateCommand(command: 'cmd+shift+alt+ctrl+b'); expect(shortcutEvent.keybindings.length, 1); expect(shortcutEvent.keybindings.first.isMetaPressed, true); @@ -35,10 +36,13 @@ void main() async { final editor = tester.editor ..insertTextNode(text) ..insertTextNode(text); + await editor.startTesting(); + await editor.updateSelection( Selection.single(path: [1], startOffset: text.length), ); + if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( key: LogicalKeyboardKey.arrowLeft, @@ -50,10 +54,12 @@ void main() async { isMetaPressed: true, ); } + expect( editor.documentSelection, Selection.single(path: [1], startOffset: 0), ); + await editor.updateSelection( Selection.single(path: [1], startOffset: text.length), ); @@ -67,6 +73,7 @@ void main() async { ); } } + if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( key: LogicalKeyboardKey.arrowLeft, @@ -78,6 +85,7 @@ void main() async { isMetaPressed: true, ); } + expect( editor.documentSelection, Selection.single(path: [1], startOffset: 0), @@ -91,10 +99,13 @@ void main() async { final editor = tester.editor ..insertTextNode(text) ..insertTextNode(text); + await editor.startTesting(); + await editor.updateSelection( Selection.single(path: [1], startOffset: 0), ); + if (Platform.isWindows || Platform.isLinux) { await editor.pressLogicKey( key: LogicalKeyboardKey.arrowRight, @@ -106,10 +117,12 @@ void main() async { isMetaPressed: true, ); } + expect( editor.documentSelection, Selection.single(path: [1], startOffset: text.length), ); + await editor.updateSelection( Selection.single(path: [1], startOffset: 0), ); @@ -123,10 +136,12 @@ void main() async { ); } } + await editor.pressLogicKey( key: LogicalKeyboardKey.arrowRight, isAltPressed: true, ); + expect( editor.documentSelection, Selection.single(path: [1], startOffset: text.length), @@ -139,10 +154,13 @@ void main() async { final editor = tester.editor ..insertTextNode(text) ..insertTextNode(text); + await editor.startTesting(); + await editor.updateSelection( Selection.single(path: [1], startOffset: text.length), ); + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { await editor.pressLogicKey( key: LogicalKeyboardKey.home, @@ -153,6 +171,7 @@ void main() async { editor.documentSelection, Selection.single(path: [1], startOffset: 0), ); + await editor.updateSelection( Selection.single(path: [1], startOffset: text.length), ); @@ -166,11 +185,13 @@ void main() async { ); } } + if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( key: LogicalKeyboardKey.home, ); } + expect( editor.documentSelection, Selection.single(path: [1], startOffset: 0), @@ -182,10 +203,13 @@ void main() async { final editor = tester.editor ..insertTextNode(text) ..insertTextNode(text); + await editor.startTesting(); + await editor.updateSelection( Selection.single(path: [1], startOffset: text.length), ); + if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { await editor.pressLogicKey( key: LogicalKeyboardKey.end, @@ -196,6 +220,7 @@ void main() async { editor.documentSelection, Selection.single(path: [1], startOffset: text.length), ); + await editor.updateSelection( Selection.single(path: [1], startOffset: 0), ); @@ -209,15 +234,50 @@ void main() async { ); } } + if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { await editor.pressLogicKey( key: LogicalKeyboardKey.end, ); } + expect( editor.documentSelection, Selection.single(path: [1], startOffset: text.length), ); }); + + testWidgets('delete sentence to beginning', (tester) async { + const text = "Hello World!"; + final editor = tester.editor + ..insertTextNode(text) + ..insertTextNode(text); + + await editor.startTesting(); + + await editor.updateSelection( + Selection.collapsed(Position(path: [1], offset: 10)), + ); + + expect(editor.documentLength, 2); + + await editor.pressLogicKey( + key: LogicalKeyboardKey.backspace, + isMetaPressed: Platform.isMacOS, + isControlPressed: !Platform.isMacOS, + isAltPressed: !Platform.isMacOS, + ); + + await tester.pumpAndSettle(); + + expect( + editor.documentSelection, + Selection.collapsed(Position(path: [1], offset: 0)), + ); + + expect(editor.documentLength, 2); + + expect((editor.document.nodeAtPath([1]) as TextNode).delta.length, 2); + }); }); }