add Vim mode service, mode commands, and status indicator

T-207, first foundation piece of the Vim layer (T-65 epic). A
VimModeService (ChangeNotifier) owns the normal/insert/visual mode and
mirrors it into the keymap as mutually-exclusive vim.normal/vim.insert/
vim.visual scope flags. Those flags are the public mode interface: the
editor (T-206) will read them to decide insert-vs-command, and vim.yaml
(T-65) guards bindings with `when: vim.*`. Nothing reaches across the
builtin boundary into the service object.

The layer is gated on the active preset — the builtin.vim extension
ties VimModeService.enabled to app.keymap.preset and re-checks on every
keymap reload, so i/v/Esc never hijack input under non-Vim presets. Mode
commands (vim.mode.{normal,insert,visual}) carry no default binding for
the same reason; only vim.yaml binds keys to them. A status-bar item
shows `-- NORMAL --` etc. while enabled.

Exposes KeymapService on the extension context so the layer can publish
scope flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 20:59:01 +02:00
co-authored by Claude Opus 4.8
parent 4aa24a9898
commit 61b969d010
12 changed files with 448 additions and 0 deletions
+2
View File
@@ -10,6 +10,7 @@ import 'package:clide/kernel/src/files.dart';
import 'package:clide/kernel/src/focus.dart';
import 'package:clide/kernel/src/i18n/i18n.dart';
import 'package:clide/kernel/src/ipc/client.dart';
import 'package:clide/kernel/src/keymap/keymap_service.dart';
import 'package:clide/kernel/src/log.dart';
import 'package:clide/kernel/src/net.dart';
import 'package:clide/kernel/src/notify.dart';
@@ -63,6 +64,7 @@ abstract class ClideExtensionContext {
PanelRegistry get panels;
LayoutArrangement get arrangement;
CommandRegistry get commands;
KeymapService get keymap;
PaletteController get palette;
ReaderNavRegistry get readerNav;
ClideClipboard get clipboard;