vim-sim
v1.0.10
Published
A complete Vim editor simulation engine for Node.js. Implements motions, operators, visual mode, text objects, macros, marks, undo/redo tree, spell checking, and more. Check out my demo here: https://vim.colefoster.ca/demo
Downloads
973
Maintainers
Readme
vim-sim
A complete, production-ready Vim editor simulation engine for Node.js. Implements the full Vim editing experience with 85%+ test coverage, including motions, operators, visual mode, text objects, macros, marks, undo/redo tree, spell checking, and more.
Core Editing
- All Basic Motions:
h,j,k,l,w,b,e,W,B,E,0,$,^,gg,G,{,},(,),% - Find & Till:
f,F,t,T,;,, - Operators:
d,c,y,>,<,=,g~,gu,gU - Visual Mode: Character (
v), Line (V), and Block (<C-v>) with all operators - Text Objects:
iw,aw,i",a",i',a',i(,a(,i{,a{,i[,a[,it,at,ip,ap,is,as - Insert Mode:
i,I,a,A,o,O,s,S,ccommands - Replace & Change:
r,R,~,J,gJ
Advanced Features
- Undo/Redo Tree: Full vim-style undo tree with branches (
u,<C-r>,g-,g+) - Macros: Record (
q) and playback (@) with count support - Marks: Local and global marks (
m,',`) - Jump List:
<C-o>,<C-i>navigation - Registers: Named, numbered, and special registers
- Spell Checking: Real-time with custom dictionaries
- Completion: Keyword, line, file, and omni completion
- Folds: Code folding support
- Multiple Windows: Split, navigate, and resize
- Multiple Buffers: File management and buffer navigation
Text Formatting
- Auto-indentation: Smart indentation based on braces
- Text Wrapping: Format text with
gq,gw - Alignment: Column alignment support
- Comment Toggling: Smart comment handling
- Digraphs: 150+ special characters
Installation
npm install vim-simimport { Session } from 'vim-sim';
// Create a new vim session
const session = new Session();
// Load some text
session.handleKey(':');
session.handleKey('e');
session.handleKey(' ');
session.handleKey('f');
session.handleKey('i');
session.handleKey('l');
session.handleKey('e');
session.handleKey('.');
session.handleKey('t');
session.handleKey('x');
session.handleKey('t');
session.handleKey('Enter');
// Or set buffer content directly
import { State, Buffer, Cursor, Mode } from 'vim-sim';
const state = new State(
new Buffer('Hello, World!\nVim simulation is awesome!'),
new Cursor(0, 0),
null,
Mode.NORMAL
);
session.setState(state);
// Execute vim commands
session.handleKey('w'); // Move to next word
session.handleKey('d'); // Start delete operator
session.handleKey('w'); // Delete word
// Get the current state
const currentState = session.getState();
console.log(currentState.buffer.content); // "Hello, is awesome!"
console.log(currentState.cursor.line); // 0
console.log(currentState.cursor.column); // 7Session
The main entry point for vim simulation. Manages state and command execution.
class Session {
constructor(config?: Partial<VimConfig>)
// Handle a single keypress
handleKey(key: string): void
// Get current editor state
getState(): State
// Set editor state
setState(state: State): void
// Get command history
getHistory(): CommandHistoryEntry[]
}Example:
const session = new Session({
tabSize: 4,
expandTab: true,
autoIndent: true
});
session.handleKey('i'); // Enter insert mode
session.handleKey('H');
session.handleKey('e');
session.handleKey('l');
session.handleKey('l');
session.handleKey('o');
session.handleKey('Escape'); // Back to normal mode
session.handleKey('0'); // Go to start of lineState
Represents the complete editor state at a point in time.
class State {
constructor(
buffer: Buffer,
cursor: Cursor,
selection: Range | null,
mode: Mode,
// ... other optional parameters
)
readonly buffer: Buffer
readonly cursor: Cursor
readonly selection: Range | null
readonly mode: Mode
readonly desiredColumn: number | null
// ... many more properties
}Buffer
Represents the text content being edited.
class Buffer {
constructor(content: string = '')
readonly content: string
getLineCount(): number
getLine(index: number): string
getLines(): string[]
}Example:
const buffer = new Buffer('Line 1\nLine 2\nLine 3');
console.log(buffer.getLineCount()); // 3
console.log(buffer.getLine(1)); // "Line 2"Cursor
Represents cursor position (0-indexed).
class Cursor {
constructor(line: number, column: number)
readonly line: number
readonly column: number
}Mode
Vim editing modes.
enum Mode {
NORMAL = 'normal',
INSERT = 'insert',
VISUAL = 'visual',
VISUAL_LINE = 'visual_line',
VISUAL_BLOCK = 'visual_block',
COMMAND_LINE = 'command_line',
REPLACE = 'replace'
}Basic Text Editing
import { Session, Buffer, State, Cursor, Mode } from 'vim-sim';
const session = new Session();
// Start with some text
session.setState(new State(
new Buffer('The quick brown fox\njumps over\nthe lazy dog'),
new Cursor(0, 0),
null,
Mode.NORMAL
));
// Delete a word: dw
session.handleKey('d');
session.handleKey('w');
console.log(session.getState().buffer.content);
// "quick brown fox\njumps over\nthe lazy dog"Visual Mode Selection
const session = new Session();
session.setState(new State(
new Buffer('Hello World'),
new Cursor(0, 0),
null,
Mode.NORMAL
));
// Visual select and delete: vlllld
session.handleKey('v'); // Enter visual mode
session.handleKey('l'); // Extend selection
session.handleKey('l');
session.handleKey('l');
session.handleKey('l');
session.handleKey('d'); // Delete selection
console.log(session.getState().buffer.content); // " World"Using Text Objects
const session = new Session();
session.setState(new State(
new Buffer('function test(arg) { return arg; }'),
new Cursor(0, 20), // Inside the parentheses
null,
Mode.NORMAL
));
// Delete inside parentheses: di(
session.handleKey('d');
session.handleKey('i');
session.handleKey('(');
console.log(session.getState().buffer.content);
// "function test() { return arg; }"Macros
const session = new Session();
session.setState(new State(
new Buffer('line1\nline2\nline3'),
new Cursor(0, 0),
null,
Mode.NORMAL
));
// Record macro: qaIHello <Esc>jq
session.handleKey('q');
session.handleKey('a');
session.handleKey('I');
session.handleKey('H');
session.handleKey('e');
session.handleKey('l');
session.handleKey('l');
session.handleKey('o');
session.handleKey(' ');
session.handleKey('Escape');
session.handleKey('j');
session.handleKey('q');
// Replay macro twice: 2@a
session.handleKey('2');
session.handleKey('@');
session.handleKey('a');
console.log(session.getState().buffer.content);
// "Hello line1\nHello line2\nHello line3"Undo/Redo Tree
const session = new Session();
const state = new State(
new Buffer('original text'),
new Cursor(0, 0),
null,
Mode.NORMAL
);
session.setState(state);
// Make some changes
session.handleKey('c');
session.handleKey('w');
session.handleKey('n');
session.handleKey('e');
session.handleKey('w');
session.handleKey('Escape');
// Undo: u
session.handleKey('u');
console.log(session.getState().buffer.content); // "original text"
// Redo: <C-r>
session.handleKey('<C-r>');
console.log(session.getState().buffer.content); // "new text"Working with Registers
const session = new Session();
session.setState(new State(
new Buffer('Copy this text\nPaste here'),
new Cursor(0, 0),
null,
Mode.NORMAL
));
// Yank to register 'a': "ayy
session.handleKey('"');
session.handleKey('a');
session.handleKey('y');
session.handleKey('y');
// Move and paste: jp
session.handleKey('j');
session.handleKey('"');
session.handleKey('a');
session.handleKey('p');
console.log(session.getState().buffer.content);
// "Copy this text\nPaste here\nCopy this text"Custom Configuration
import { Session, ConfigManager } from 'vim-sim';
const session = new Session({
tabSize: 2,
expandTab: true,
autoIndent: true,
smartIndent: true,
textWidth: 80,
wrap: true,
spell: true
});
// Or update config after creation
const config = session.getState().configManager;
// Access config values as neededSpell Checking
const session = new Session();
const state = session.getState();
// Enable spell checking
state.spellChecker.enable();
// Add custom words
state.spellChecker.addGoodWord('vim-sim');
// Check a word
const isCorrect = state.spellChecker.isCorrect('hello');
// Get suggestions
const suggestions = state.spellChecker.suggest('wrold');
console.log(suggestions); // ['world']Window Management
const session = new Session();
const state = session.getState();
// Split window horizontally: <C-w>s
// Split window vertically: <C-w>v
// Navigate windows: <C-w>h/j/k/l
// Close window: <C-w>c
// Access window manager directly
const windowManager = state.windowManager;
const activeWindow = windowManager.getActiveWindow();This package is written in TypeScript and provides full type definitions.
import type { State, Buffer, Cursor, Mode, Range, Command } from 'vim-sim';
function processVimState(state: State): void {
const { buffer, cursor, mode, selection } = state;
if (mode === Mode.VISUAL && selection) {
console.log(`Selected from ${selection.start} to ${selection.end}`);
}
}