Skip to content

lajarre/pi-vim

Repository files navigation

pi-vim — Vim Mode for Pi REPL

Modal vim-like editing for Pi's REPL prompt. Focus: the high-frequency 90% command surface, not full Vim.

TL;DR

  • Problem: REPL editing is slow with only linear cursor movement.
  • Solution: modal editing (INSERT/NORMAL) with Vim-style motions, operators, counts, and repeatable workflows.
  • Install: pi install npm:pi-vim

Install

pi install npm:pi-vim

Restart Pi after install.

Local loading (dev)

pi --extension /path/to/pi-vim/index.ts

Or add to .pi/settings.json:

{
  "extensions": ["./pi-extensions/pi-vim/index.ts"]
}

30-second quickstart

Try on multi-line input:

Esc        # NORMAL mode
3gg        # jump to absolute line 3
2dw        # delete two words
u          # undo
2}         # jump two paragraphs forward

Mode indicator (INSERT / NORMAL) appears at bottom-right.

Why pi-vim

  • Fast modal editing without leaving Pi.
  • Count-aware motions/operators (2dw, 3G, d2j, 2}).
  • Strong REPL-focused defaults; safe out-of-scope boundaries documented.
  • Clipboard/register behavior is explicit and tested.

For you / not for you

Use pi-vim if you want fast Vim muscle-memory in Pi prompts. Skip it if you need full Vim feature parity (visual mode, macros, search, ex-commands, etc.).

Common recipes

Goal Keys
Jump to exact line 25 25gg (or 25G)
Delete two words 2dw
Change to end of line C
Delete current + 2 lines below d2j
Yank 3 lines 3yy
Join 3 lines with spacing 3J
Jump 2 paragraphs forward 2}
Undo last edit u

Full reference

Mode switching

Key Action
Esc Insert → Normal mode
Esc Normal mode → pass to Pi (abort agent)
i Normal → Insert at cursor
a Normal → Insert after cursor
I Normal → Insert at line start
A Normal → Insert at line end
o Normal → open line below + Insert
O Normal → open line above + Insert

Insert-mode shortcuts (stay in Insert mode):

Key Action
Shift+Alt+A Go to end of line
Shift+Alt+I Go to start of line
Alt+o Open line below
Alt+Shift+O Open line above

Navigation (Normal mode)

A {count} prefix can be prepended to any navigation key (max: 9999).

Key Action
h Left
l Right
j Down
k Up
{count}h/l Move left/right {count} cols
{count}j/k Move down/up {count} lines (clamped to buffer size)
0 Line start
$ Line end
gg Buffer start (line 1)
{count}gg Go to line {count} (1-indexed, clamped)
G Buffer end (last line)
{count}G Go to line {count} (1-indexed, clamped)
w Next word start (keyword/punctuation aware)
b Previous word start
e word end (inclusive)
W Next WORD start (whitespace-delimited token)
B Previous WORD start
E WORD end (inclusive)
{count}w/b/e Move {count} word motions
{count}W/B/E Move {count} WORD motions
} Move to next paragraph start (line start col 0)
{ Move to previous paragraph start (line start col 0)
{count}} Repeat } {count} times
{count}{ Repeat { {count} times

word (w/b/e) splits punctuation from keyword chars. WORD (W/B/E) treats any non-whitespace run as one token (foo-bar, path/to, x.y).

Paragraph boundary definition (this extension wave):

  • blank line: matches ^\s*$
  • paragraph start: non-blank line at BOF, or non-blank line immediately after a blank line

Standalone { / } motions are navigation-only (no text/register mutation). Counted forms ({count}{, {count}}) step paragraph-by-paragraph. If no further paragraph boundary exists, motions clamp at BOF/EOF. Operator forms with braces (d{, d}, c{, c}, y{, y}) are out of scope for this wave.


Character-find motions (Normal mode)

A {count} prefix finds the Nth occurrence of {char} on the line.

Key Action
f{char} Jump forward to char (inclusive)
F{char} Jump backward to char (inclusive)
t{char} Jump forward to one before char (exclusive)
T{char} Jump backward to one after char (exclusive)
{count}f{char} Jump to Nth occurrence of char forward
; Repeat last f/F/t/T motion
, Repeat last motion in reverse direction

Char-find motions compose with operators: df{char}, ct{char}, d{count}t{char}, etc.


Edit operators (Normal mode)

All operators write to the unnamed register and mirror to the system clipboard (best-effort; clipboard failure never breaks editing).

Delete d{motion} / dd

A {count} or dual-count prefix ({pfx}d{op}{motion}) is supported for word, char-find, and linewise motions. Maximum total count: 9999.

Command Deletes
dw Forward to next word start (exclusive, can cross lines)
de Forward to word end (inclusive, can cross lines)
db Backward to word start (exclusive, can cross lines)
dW Forward to next WORD start (exclusive, can cross lines)
dE Forward to WORD end (inclusive, can cross lines)
dB Backward to WORD start (exclusive, can cross lines)
d{count}w/e/b Forward/backward {count} word motions
d{count}W/E/B Forward/backward {count} WORD motions
d$ To end of line
d0 To start of line
dd Current line (linewise)
{count}dd {count} lines (linewise)
d{count}j Current line + {count} lines below (linewise)
d{count}k Current line + {count} lines above (linewise)
dG Current line to end of buffer (linewise)
df{char} To and including char
d{count}f{char} To and including Nth char
dt{char} Up to (not including) char
dF{char} Backward to and including char
dT{char} Backward to one after char
diw Inner word
daw Around word (includes surrounding spaces)
d{count}aw Around {count} words

Change c{motion} / cc

Same motion and count set as d. Deletes text then enters Insert mode.

Command Action
cw Change word + Insert
ce / cb Change to word end / previous word start
cW Change WORD + Insert (cW on non-space behaves like cE)
cE / cB Change to WORD end / previous WORD start
c{count}w/e/b Change {count} word motions + Insert
c{count}W/E/B Change {count} WORD motions + Insert
ciw Change inner word
caw Change around word
cc Delete line content + Insert
c$ Delete to EOL + Insert
All d motions apply

Single-key edits

A {count} prefix is supported for x, p, P. Maximum: 9999.

Key Action
x Delete char under cursor (no-op at/past EOL)
{count}x Delete {count} chars
s Delete char under cursor + Insert mode
S Delete line content + Insert mode
D Delete cursor to EOL (captures \n if at EOL with next line)
C Delete cursor to EOL + Insert mode

Yank y{motion} / yy

Same motion set as d. Writes to register, no text mutation.

Command Yanks
yy Whole line + trailing \n
{count}yy {count} whole lines + trailing \n
y{count}j Current line + {count} lines below (linewise)
y{count}k Current line + {count} lines above (linewise)
yG Current line to end of buffer (linewise)
yw Forward to next word start
ye To word end (inclusive)
yb Backward to word start
yW Forward to next WORD start
yE To WORD end (inclusive)
yB Backward to WORD start
y$ To end of line
y0 To start of line
yf{c} To and including char
yiw Inner word
yaw Around word (includes spaces)

Counted yank caveat: counted word/WORD yank motions are intentionally not implemented (y2w, 2yw, y2W, 2yW, etc. cancel the pending operator). Linewise counted yank ({count}yy, y{count}j/k) remains supported.


Put / Paste

Key Action
p Put after cursor (char-wise) / new line below (line-wise)
P Put before cursor (char-wise) / new line above (line-wise)
{count}p Put {count} times after cursor
{count}P Put {count} times before cursor

Put reads from the unnamed register (not OS clipboard).
Line-wise detection: register content ending in \n is treated as line-wise.


Undo

Key Action
u Undo — sends ctrl+_ (\x1f) to the underlying readline editor

Redo (<C-r>) is not implemented — see Out of scope.


Register and clipboard policy

  • One unnamed register (like Vim's "" register).
  • Every d, c, x, s, S, D, C, y operator form (including dd, {count}dd, d{count}j/k, dG, yy, {count}yy, y{count}j/k, yG) writes to the register and mirrors to the OS clipboard (via copyToClipboard, best-effort).
  • p / P read from the unnamed register only (not the OS clipboard).
  • This gives stable behaviour across local terminals and SSH / OSC52 setups.

Known differences from full Vim

Area This extension Full Vim
$ motion Moves past last char (readline CTRL+E) Moves to last char
w / e / b + W / E / B Cross-line for word + WORD motions Cross-line
0 / $ operators Exclusive of anchor col 0 inclusive of col 0
Undo depth Delegates to underlying readline undo Full per-change undo tree
Redo Not implemented <C-r>
Visual mode Not implemented v, V, <C-v>
Text objects Supports iw/aw only Full text-object set
Count prefix Supported for operators, word/char motions, navigation, and edits (x, p/P); capped at MAX_COUNT=9999 to prevent abuse Full support
Named registers Not implemented ("a, etc.) Supported
Macros Not implemented (q, @) Supported
Search Not implemented (/, ?, n, N) Supported
Ex commands Not implemented (:s, :g, etc.) Supported
Multi-line operators Supports d/c/y with w/e/b and W/E/B, plus j/k counts and G; not full Vim motion matrix Rich cross-line semantics

Out of scope

These are explicitly deferred and not planned for this feature:

  • Visual modes (v, V, block visual)
  • Extended text objects beyond word (ip, i", i(, etc.)
  • Named registers ("a, "b, …)
  • Macros (q{char}, @{char})
  • Ex command surface (:s, :g, :r, …)
  • Search mode (/, ?, n, N)
  • Repeat (.)
  • Extended count prefix beyond currently supported motions (e.g. :, global operator counts)
  • Redo (<C-r>) — no native redo primitive in the underlying readline editor; deferred until a suitable hook is available.
  • Window / tab / buffer management
  • Plugin / runtime ecosystem compatibility

Architecture notes

  • index.tsModalEditor subclass of CustomEditor; all key handling.
  • motions.ts — pure motion calculation helpers (findWordMotionTarget, findCharMotionTarget); no side effects.
  • types.ts — shared types and escape-sequence constants.
  • test/ — Node test runner suite; no browser / full runtime required.

Run tests:

cd pi-vim
npm test

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors