diff --git a/test/builtin/editor/vim_edit_ops_test.dart b/test/builtin/editor/vim_edit_ops_test.dart index 12a5a8ff..ba77b1cc 100644 --- a/test/builtin/editor/vim_edit_ops_test.dart +++ b/test/builtin/editor/vim_edit_ops_test.dart @@ -149,6 +149,43 @@ void main() { expect(r.enterInsert, isTrue); expect(r.register?.linewise, isTrue); }); + + test('cw deletes to the next word and enters insert', () { + final r = applyVim(VimAction.changeWord, _tev('hello world', 0)); + expect(r.value.text, 'world'); + expect(r.enterInsert, isTrue); + expect(r.register?.text, 'hello '); + }); + + test('x at the line end is a no-op', () { + final r = applyVim(VimAction.deleteChar, _tev('ab\ncd', 2)); // caret on the newline + expect(r.value.text, 'ab\ncd'); + expect(r.register, isNull); + }); + + test('D at the line end is a no-op', () { + final r = applyVim(VimAction.deleteToEnd, _tev('ab', 2)); + expect(r.value.text, 'ab'); + }); + + test('p with an empty register does nothing', () { + final r = applyVim(VimAction.paste, _tev('ab', 0)); + expect(r.value.text, 'ab'); + }); + }); + + group('word-motion edges', () { + test('w / b cross punctuation and underscores as their own class', () { + // foo_bar is one word (underscore is a word char); the dot is punct. + const t = 'foo_bar.baz'; + expect(applyVim(VimAction.wordForward, _tev(t, 0)).value.selection.extentOffset, 7); // the '.' + expect(applyVim(VimAction.wordBackward, _tev(t, 8)).value.selection.extentOffset, 7); + }); + + test('e on the last word clamps to the final char', () { + final r = applyVim(VimAction.wordEnd, _tev('hi', 1)); + expect(r.value.selection.extentOffset, 1); + }); }); group('insert entry', () { diff --git a/test/builtin/keybindings_ui/preset_commands_test.dart b/test/builtin/keybindings_ui/preset_commands_test.dart index 0365f1a0..7aea256d 100644 --- a/test/builtin/keybindings_ui/preset_commands_test.dart +++ b/test/builtin/keybindings_ui/preset_commands_test.dart @@ -17,6 +17,20 @@ void main() { }); tearDown(() => f.dispose()); + test('identifies itself', () { + final ext = KeybindingsUiExtension(); + expect(ext.id, 'builtin.keybindings-ui'); + expect(ext.title, 'Keybindings UI'); + expect(ext.version, '0.1.0'); + }); + + test('deactivate drops the keymap reference', () async { + await f.services.extensions.deactivate('builtin.keybindings-ui'); + // Re-activating is clean (no retained state). + await f.services.extensions.activate('builtin.keybindings-ui'); + expect(f.services.commands.get('keymap.preset.vim'), isNotNull); + }); + test('contributes a switch command per shipped preset', () { expect(f.services.commands.get('keymap.preset.vim'), isNotNull); expect(f.services.commands.get('keymap.preset.default'), isNotNull); diff --git a/test/builtin/vim/vim_extension_test.dart b/test/builtin/vim/vim_extension_test.dart index bfe77ae1..d9b1f955 100644 --- a/test/builtin/vim/vim_extension_test.dart +++ b/test/builtin/vim/vim_extension_test.dart @@ -22,6 +22,12 @@ void main() { bool flag(String name) => f.services.keymap.scope[name] ?? false; + test('identifies itself', () { + expect(ext.id, 'builtin.vim'); + expect(ext.title, 'Vim'); + expect(ext.version, '0.1.0'); + }); + test('inert under the default preset', () async { await f.services.extensions.activate('builtin.vim'); expect(ext.modeService!.enabled, isFalse); diff --git a/test/kernel/src/keymap/sequence_matcher_test.dart b/test/kernel/src/keymap/sequence_matcher_test.dart index 9fae0b63..db249c44 100644 --- a/test/kernel/src/keymap/sequence_matcher_test.dart +++ b/test/kernel/src/keymap/sequence_matcher_test.dart @@ -171,5 +171,13 @@ void main() { test('flush with nothing buffered is a no-op', () { expect(matcher.flush().outcome, SeqOutcome.pending); }); + + test('flush of a lone prefix key (no exact) passes it through', () { + // `g` is a pure prefix of `g g` with no single-`g` binding. + expect(feed('g').outcome, SeqOutcome.pending); + final r = matcher.flush(); + expect(r.outcome, SeqOutcome.unmatched); + expect(r.passKey, KeyChord.parse('g')); + }); }); }