MODULO 4.5

✏️ Vim Mode

Uma engine completa de vim keybinding implementada em TypeScript puro -- maquinas de estado, funcoes puras e zero compromisso com corretude. Cinco arquivos, ~700 linhas.

8
Topicos
~75
Minutos
Deep
Nivel
Source
Tipo
1

📁 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

2

📝 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.

3

🗺️ 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
                    
4

➡️ Motions -- Pure Cursor Math

Categoria Keys Metodo
Characterh lleft() / right()
Line (logical)j kdownLogicalLine() / upLogicalLine()
Line (visual)gj gkdown() / up()
Word (small-w)w b enextVimWord() / prevVimWord() / endOfVimWord()
Word (WORD)W B EnextWORD() / prevWORD() / endOfWORD()
Line positions0 ^ $startOfLogicalLine() / firstNonBlank() / endOfLogicalLine()
FileG ggstartOfLastLine() / 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.

5

✂️ Operators & Text Objects

Operadores

d -- delete
c -- change (delete + enter INSERT)
y -- yank (copy)

Combinacoes

dw -- operator + motion
dfx -- operator + find
diw -- operator + text object
dd -- 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.

6

🔍 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.

7

✖ 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
8

🎯 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.

📋 Resumo do Modulo

✅ Engine vim e uma maquina de estados TypeScript pura -- sem imports de UI, side effects via callbacks de contexto
✅ CommandState como discriminated union que funciona como documentacao; exhaustive checking do TypeScript garante completude
✅ Motions sao funcoes puras; operators compoem por cima convertendo delta de cursor em byte range
✅ Text objects usam algoritmos diferentes para pares simetricos (quotes) vs assimetricos (brackets)
✅ Compound counts (2d3w) via estado dedicado operatorCount com multiplicacao
✅ Dot-repeat funciona porque toda mutacao grava um RecordedChange com dados suficientes para replay
✅ Segmentacao de graphemes via Intl.Segmenter torna a engine unicode-safe
← Modulo Anterior Proximo Modulo →