📁 Cinco Arquivos da Engine
types.ts
Tipos de maquina de estados -- todos os modos, estados e changes gravados como unions TypeScript
transitions.ts
Tabela de transicao -- uma funcao por estado, recebe keypress e retorna proximo estado e/ou side-effect
motions.ts
Matematica pura de cursor -- funcoes stateless que resolvem motion key + count para um Cursor alvo
operators.ts
Mutacoes de texto -- delete / change / yank aplicados a ranges de caracteres, linewise ou text objects
textObjects.ts
Selecao estrutural -- iw, aw, i(, a", etc. com seguranca de grapheme
📝 O Type System como Especificacao
VimState e uma discriminated union com exatamente dois membros:
export type VimState =
| { mode: 'INSERT'; insertedText: string }
| { mode: 'NORMAL'; command: CommandState }
CommandState tem 11 variantes, cada uma codificando exatamente o que o parser precisa lembrar mid-sequence:
export type CommandState =
| { type: 'idle' }
| { type: 'count'; digits: string }
| { type: 'operator'; op: Operator; count: number }
| { type: 'operatorCount'; op: Operator; count: number; digits: string }
| { type: 'operatorFind'; op: Operator; count: number; find: FindType }
| { type: 'operatorTextObj'; op: Operator; count: number; scope: TextObjScope }
| { type: 'find'; find: FindType; count: number }
| { type: 'g'; count: number }
| { type: 'operatorG'; op: Operator; count: number }
| { type: 'replace'; count: number }
| { type: 'indent'; dir: '>' | '<'; count: number }
💡 Insight de Design
Exhaustive switch-checking do TypeScript garante que se um novo estado e adicionado a CommandState, cada switch que o manipula produz erro de compilacao ate que um handler seja adicionado.
🗺️ Maquina de Estados
stateDiagram-v2
[*] --> idle
idle --> count : digit 1-9
idle --> operator : d / c / y
idle --> find : f F t T
idle --> g : g
idle --> replace : r
idle --> indent : > or <
idle --> idle : execute (motion / action)
count --> count : digit 0-9
count --> operator : d / c / y
count --> idle : execute (motion / action)
operator --> operatorCount : digit
operator --> operatorTextObj : i / a
operator --> operatorFind : f F t T
operator --> operatorG : g
operator --> idle : execute (dd cc yy or motion)
operatorCount --> operatorCount : digit
operatorCount --> idle : execute
operatorFind --> idle : execute (char received)
operatorTextObj --> idle : execute (obj type received)
find --> idle : execute (char received)
g --> idle : execute (gg / gj / gk)
operatorG --> idle : execute or cancel
replace --> idle : execute (char received)
indent --> idle : execute
➡️ Motions -- Pure Cursor Math
| Categoria | Keys | Metodo |
|---|---|---|
| Character | h l | left() / right() |
| Line (logical) | j k | downLogicalLine() / upLogicalLine() |
| Line (visual) | gj gk | down() / up() |
| Word (small-w) | w b e | nextVimWord() / prevVimWord() / endOfVimWord() |
| Word (WORD) | W B E | nextWORD() / prevWORD() / endOfWORD() |
| Line positions | 0 ^ $ | startOfLogicalLine() / firstNonBlank() / endOfLogicalLine() |
| File | G gg | startOfLastLine() / startOfFirstLine() |
Early Break
O loop em resolveMotion aplica um passo por vez, interrompendo cedo quando o cursor para de mover. Isso trata corretamente bater no inicio/fim do buffer mid-count.
✂️ Operators & Text Objects
Operadores
d -- deletec -- change (delete + enter INSERT)y -- yank (copy)Combinacoes
dw -- operator + motiondfx -- operator + finddiw -- operator + text objectdd -- operator doubled (line)⚠️ Caso Especial: cw
Vim real trata cw diferente de dw: muda ate o fim da palavra, nao o inicio da proxima. getOperatorRange() trata isso explicitamente.
🔍 Text Objects -- Bracket vs Quote
Brackets (assimetricos)
Scan bidirecional com contagem de profundidade.
( )
[ ]
{ }
< >
Quotes (simetricos)
Sem nesting -- encontra todas as posicoes e pareia 0-1, 2-3, 4-5.
"
'
`
💡 Unicode Safety
O word object finder pre-segmenta o texto em graphemes usando Intl.Segmenter antes de escanear -- garantindo corretude com caracteres unicode multi-byte.
✖ Compound Counts: 2d3w
Vim real suporta counts compostos: 2d3w deleta 6 palavras (2 x 3). O estado operatorCount existe exclusivamente para isso.
const effectiveCount = state.count * motionCount // multiplicacao
// MAX_VIM_COUNT = 10000 previne catastrofe de performance
🎯 Dependency Inversion
Nenhum dos arquivos vim importa React, Ink ou qualquer framework de UI. Todos os side effects fluem atraves de dois tipos de contexto:
type OperatorContext = {
cursor, text, setText, setOffset,
enterInsert, getRegister, setRegister,
getLastFind, setLastFind, recordChange
}
type TransitionContext = OperatorContext & {
onUndo?, onDotRepeat?
}
🏆 Vitoria Arquitetural
A engine vim tem zero conhecimento de como texto e armazenado, renderizado ou transmitido para a API. Opera puramente atraves das interfaces de contexto. Testavel unitariamente sem iniciar um terminal.