Skip to content

Vim compat 2/9: make contested key defaults derive from editor_mode — "Vim mode" currently does not mean Vim #800

Description

@JDonaghy

Part of the Vim-compatibility chain (issue 2 of 9). Chained after the oracle-harness issue.

Decision this implements

EditorMode { Vim, Vscode } already exists (src/core/settings.rs:20), and editor_mode already defaults to EditorMode::Vim (settings.rs:761).

But editor_mode is referenced zero times in src/core/engine/keys.rs. The contested defaults are unconditional constants:

Setting Current default settings.rs
ctrl_f_action "find" — opens find/replace, even in Normal mode :335
auto_pairs true — typing ( in insert yields () :355
completion_keys.accept "Tab" — the popup eats <Tab> :658

So "Vim mode" is already the shipped default and already does not mean Vim. A user pressing <C-f> to page down gets a find dialog.

Agreed design: mode-derived defaults. Each default_*() becomes a function of EditorMode. Vim mode yields strict Vim values; Vscode mode yields today's IDE values. Every setting stays independently overridable, so :set mode=vim followed by :set auto_pairs=true is a valid, supported combination.

Agreed ship default: EditorMode::Vim — unchanged from today, but now honest.

fn default_ctrl_f_action(mode: EditorMode) -> String {
    match mode {
        EditorMode::Vim    => "page".to_string(),  // <C-f> pages down
        EditorMode::Vscode => "find".to_string(),  // opens find/replace
    }
}

What to do

  1. Introduce a mode-aware defaults layer in src/core/settings.rs. Serde's #[serde(default = "...")] cannot see sibling fields, so resolve in two steps: deserialize contested fields as Option<T>, then fill None from the mode in a post-deserialize pass (Settings::apply_mode_defaults(&mut self)), called after load and whenever editor_mode changes at runtime.
  2. An explicit user setting must always win over the mode default, including when the mode is changed later. Distinguish "unset, inherit from mode" from "explicitly set to the value that happens to equal a mode default" — otherwise :set mode=vscode silently discards a user's explicit auto_pairs=false.
  3. Convert these three to mode-derived: ctrl_f_action, auto_pairs, completion_keys.accept. Later issues in this chain will add more; this issue establishes the mechanism and is deliberately small.
  4. :set mode=vim / :set mode=vscode must re-resolve unset fields live, without a restart.
  5. Document the mechanism and the full contested-defaults table in docs/PATTERNS.md (it already covers "adding new settings").

Migration

This changes behaviour on upgrade for existing users on the default config: <C-f> stops opening find, ( stops auto-pairing, <Tab> stops accepting the completion popup. Call it out in the release notes with the one-line escape hatch (:set mode=vscode).

Acceptance

  • With no config file: mode=vim, ctrl_f_action=page, auto_pairs=false, completion_keys.accept does not capture <Tab>.
  • With mode=vscode and nothing else set: all three revert to today's IDE values.
  • :set mode=vim + :set auto_pairs=true keeps autopairs on and everything else strict; a later :set mode=vscode does not clobber that explicit true.
  • Unit tests in settings.rs for each of the three fields × two modes × (unset, explicitly set).
  • A TuiDriver black-box test in src/tui_main/shell_app.rs: in default (Vim) mode, <C-f> scrolls the rendered viewport rather than opening the find overlay. Assert on rendered output, not on a state field.
  • docs/PATTERNS.md updated.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    conformanceVim conformance testingcoordTracked by coord-tui pipelineenhancementNew feature or requeststatus:readyRefined and ready to enter the work pipeline

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions