Skip to content

Vi Mode for Fresh

Vi-style modal editing implemented as a TypeScript plugin with minimal core changes.

Design Philosophy

  1. Plugin-based - Vi logic lives in plugins/vi_mode.ts, not Rust
  2. Composable - Operators (d, c, y) work with any motion
  3. Minimal core - Rust provides atomic actions, plugin handles mode logic
  4. Community-friendly - TypeScript changes don't require recompilation

How It Works

Atomic Actions (Preferred)

For common operator+motion combinations, we use atomic Rust actions that perform the entire operation in one step. This avoids async timing issues.

typescript
const atomicOperatorActions = {
  d: {
    move_word_right: "delete_word_forward",
    move_word_left: "delete_word_backward",
    move_line_end: "delete_to_line_end",
    move_line_start: "delete_to_line_start",
  },
  y: {
    move_word_right: "yank_word_forward",
    move_word_left: "yank_word_backward",
    move_line_end: "yank_to_line_end",
    move_line_start: "yank_to_line_start",
  },
};

Selection-Based Fallback

For motions without atomic actions, we use selection-based approach:

typescript
function applyOperatorWithMotion(operator: string, motionAction: string) {
  editor.executeAction(selectAction);  // e.g., select_up, select_down
  editor.executeAction("cut");         // or "copy" for yank
}

This works because selections are synchronous within a single plugin action.

Batch Actions API

For efficient count prefix support (e.g., 3dw), we use executeActions which executes multiple actions in a single Rust call without roundtrips:

typescript
// Execute "move_down" 5 times efficiently
editor.executeActions([{ action: "move_down", count: 5 }]);

// Execute multiple actions in sequence
editor.executeActions([
  { action: "delete_word_forward", count: 3 },
  { action: "move_right" }
]);

Current Status

Working

FeatureCommands
Movementh j k l, w b e, 0 $, gg G, Ctrl-f Ctrl-b
Count prefix3j, 5w, 3dw, 2dd, 10x - works with motions, operators, and more
Operatorsd c y + motions (dw, cw, etc.)
Line opsdd cc yy, D C
Char opsx X s
Find charf t F T, ; ,
Visual modev (char), V (line), Ctrl-v (block) - select with motions, then d/c/y
Text objectsiw aw (word), i" a" i' a' (quotes), i( a( i{ a{ i[ a[ (brackets)
Inserti a I A o O
Search/ n N
Colon cmds:w :q :wq :q! :e :sp :vs :bn :bp :<line> and more
Repeat. - repeat last change (works with x, dd, dw, cw, insert, etc.)
Otheru Ctrl-r (undo/redo), p P (paste), % (bracket match)

Not Implemented

FeaturePriorityNotes
RegistersLow"a, "b, named registers
MacrosLowq, @

Colon Command Mode

Press : in normal mode to enter command mode. Type a command and press Enter to execute.

Supported Commands

CommandAliasesDescription
:w:writeSave current file
:q:quitClose buffer (fails if modified)
:q!Force close buffer (discard changes)
:wq:xSave and close buffer
:wa:wallSave all buffers
:qa:qallClose all buffers
:qa!Force close all buffers
:wqa:xaSave all and quit
:e:editReload current file
:e <file>Open file
:sp:splitHorizontal split
:vs:vsplitVertical split
:onlyClose other splits
:newNew buffer in horizontal split
:vnewNew buffer in vertical split
:enewNew buffer in current split
:bn:bnextNext buffer
:bp:bprevPrevious buffer
:bd:bdeleteClose buffer
:ls:buffers, :filesList buffers
:tabnew:tabeNew tab/buffer
:tabn:tabnextNext tab
:tabp:tabprevPrevious tab
:cn:cnextNext diagnostic
:cp:cprevPrevious diagnostic
:copen:copeOpen diagnostics panel
:<number>Go to line number
:set nu:set numberShow line numbers
:set nonu:set nonumberHide line numbers
:noh:nohlsearchClear search highlight
:pwdPrint working directory
:f:fileShow file info
:versionShow version
:help:hShow help

Not Supported

  • :!command - Shell commands (use terminal)
  • :s/old/new/ - Substitute (use Ctrl+H for search/replace)
  • :g/pattern/ - Global command
  • Range prefixes (e.g., :%, :1,10)

Files

FilePurpose
plugins/vi_mode.tsPlugin implementation (~900 lines)
src/input/buffer_mode.rsPlugin mode key handling
src/services/plugins/runtime.rsCore APIs for plugins
tests/e2e/vi_mode.rsE2E tests

Usage

  1. Open command palette (Ctrl+P)
  2. Run "Toggle Vi mode"
  3. Status bar shows current mode (-- NORMAL --, -- INSERT --)

Released under the Apache 2.0 License