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