- Devido às limitações do editor Howl (desenvolvimento descontinuado, busca lenta, incompatibilidade com SSH e falta de suporte a terminal), o autor decidiu desenvolver por conta própria um novo editor de texto em TUI
- Tentou 13 editores, incluindo Helix, VS Code, Vim, Neovim e Emacs, mas nenhum oferecia a sensação de uso (Fingerspitzengefühl) que ele buscava
- No início, implementou apenas o mínimo de funcionalidades personalizado para uso pessoal, deixando desempenho, Unicode e suporte multilíngue para depois, e expandindo gradualmente
- Durante o desenvolvimento, implementou diretamente seu próprio motor de regex, navegador de arquivos, renderização baseada em TUI e integração com buffer de terminal
- Aplicou diversas técnicas de otimização de desempenho em busca no projeto inteiro, destaque de sintaxe, tratamento de cache e distribuição de trabalho multithread
- Como resultado, concluiu uma ferramenta perfeitamente adaptada ao seu próprio fluxo de trabalho e afirma ter recuperado a produtividade e o prazer de programar
Limitações dos editores existentes e busca por alternativas
- Os problemas do editor Howl, usado por cerca de 10 anos, foram o gatilho para começar a desenvolver um editor próprio
- O desenvolvimento estava parado havia anos, então ele mantinha seu próprio fork, mas como o código era escrito em MoonScript, fazer mudanças profundas era difícil
- O desempenho da busca de arquivos em todo o projeto era insuficiente, interrompendo o fluxo de trabalho
- Por ser um editor GUI, não era possível usá-lo remotamente via conexão SSH
- Não havia terminal integrado, então não dava para interagir ao vivo com comandos externos, e a maior parte dos códigos de escape ANSI não era suportada
- Testou 13 editores, incluindo Helix, VS Code, Sublime Text, Vim, Zed, Neovim, Emacs, Geany, Micro, Lite XL, Lapce, GNOME Builder e Kakoune
- Cada um tinha seus pontos fortes, mas nenhum atendia à sensação de uso (Fingerspitzengefühl) desejada
- O Helix foi o que usou por mais tempo, mas perdeu o interesse depois de um mês
Estratégia inicial de desenvolvimento
- No começo, limitou o escopo ao mínimo possível
- Excluiu funcionalidades para outros usuários e deixou toda a configuração hardcoded
- Adiou otimizações de desempenho e começou com um buffer baseado em
String
- Também deixou de lado o suporte completo a grafemas Unicode, julgando suficiente que o símbolo
£ ocupasse uma única coluna
- O destaque de sintaxe suportava só algumas poucas linguagens de uso frequente; para o restante, usou um destaque genérico baseado em delimitadores
- Na segunda tentativa, construiu primeiro um pequeno framework TUI, mas com o tempo removeu a maior parte dele e mudou para uma abordagem mais direta e detalhada
Dogfooding na prática
- Depois que o editor atingiu o limiar mínimo de funcionalidade para abrir, editar e salvar um único arquivo, ele passou a seguir três práticas
- Em vez de
nano, passou a usar seu próprio editor de forma forçada para editar arquivos do sistema ou escrever notas
- Sempre que encontrava funcionalidades faltando, bugs, comportamentos estranhos ou limitações, registrava tudo no
README.md do projeto
- Problemas suficientemente irritantes eram corrigidos imediatamente
- Essas três práticas fizeram o volume de trabalho crescer de 1 hora por mês para várias horas por semana
- De um total de cerca de 10.000 linhas de código, quase tudo foi escrito nos últimos 6 meses
Manipulação de cursor
- A manipulação de cursor é uma área difícil de implementar
- Combinações de teclas como
ctrl + shift + left parecem naturais para o usuário, mas a lógica por trás delas é complexa
- O conselho central é implementar entradas de alto nível como combinações de operações primitivas
- Ex.: backspace por palavra → mover o cursor por palavra + selecionar intervalo + deletar
- Ao implementar undo/redo, essas 3 ações precisam ser agrupadas como uma só para garantir um resultado intuitivo
- Isso ajudou a entender por que editores modais expõem diretamente essas operações primitivas ao usuário
Navegador de arquivos
- O navegador de arquivos do Howl era o principal motivo pelo qual ele não conseguia migrar para outro editor
- O filtro fuzzy com atualização imediata era excelente, permitindo encontrar o arquivo desejado quase sempre com apenas 1 ou 2 teclas
- Se o arquivo não existisse, era possível criá-lo inline
- Ao digitar
~/, o navegador mudava automaticamente para o diretório home
- Também mostrava uma prévia do arquivo a ser aberto na janela principal de edição
- Critica a forma como outros editores resolvem a abertura de arquivos com dependência de mouse, diálogos padrão do GTK e adivinhação de nomes de arquivo
- Na implementação própria, concluiu que três critérios simples bastavam, em vez de métodos complexos como Levenshtein distance
- Se o nome começa com a expressão de filtro
- Se o nome contém a expressão de filtro
- O horário mais recente de modificação/acesso
- Permitiu correspondência sem distinção entre maiúsculas e minúsculas, mas com um leve ganho de prioridade quando as maiúsculas/minúsculas coincidiam exatamente
- Mesmo em projetos com dezenas de milhares de arquivos, após digitar 2 teclas havia cerca de 95% de chance de o arquivo desejado estar entre os 2 primeiros resultados
Motor de regex
- Regex é usada em três lugares: busca no projeto inteiro, destaque de sintaxe e busca dentro do buffer
- Razões para implementar o motor por conta própria em vez de usar o crate
regex-automata
- Precisava lidar com edge cases sensíveis ao contexto, como a sintaxe de raw strings do Rust
- O próprio projeto também era um exercício de construir e entender sua própria stack
- A implementação inicial era lenta: analisava a sintaxe regex com o crate de parsing
chumsky e percorria a AST a cada caractere
- Depois, foi realizando otimizações em etapas
- Otimizador single-pass: converte grupos recorrentes de correspondência de caracteres em um único nó
String, permitindo buscas exatas por string
- Extração de prefixo comum: por exemplo, em
hel[(lo)p], detecta o prefixo comum hel e tenta casar apenas nessas posições → grande ganho na busca pelo projeto inteiro
- Reimplementou o walker da AST como uma VM de threaded code baseada em chamadas dinâmicas do Rust
- Converteu essa VM para o formato CPS (Continuation-Passing Style), em que cada instrução faz tail-call da próxima, aproveitando otimizações do compilador
- Encapsulou as lentas chamadas dinâmicas de função do Rust sem lookup de vtable, reduzindo o codegen de muitos comandos regex a poucas instruções de máquina
- Sempre que possível, implementou comandos regex em nível de byte em vez de codepoints Unicode; graças ao design do UTF-8, técnicas de otimização para ASCII também funcionavam em codepoints multibyte
- Também tentou compilar para uma cadeia de jump LUTs, mas benchmarks mostraram ganho de apenas 20–30% sobre threaded code, com perda grande de flexibilidade, então não adotou
- Resultado final: no destaque de sintaxe mais complexo para Rust, conseguiu destacar por completo um arquivo gerado automaticamente de 50.000 linhas em menos de 10 ms a partir de um estado limpo
Cache de destaque de sintaxe
- No início, a estratégia era redestacar o arquivo inteiro a cada mudança, mas isso causava queda de desempenho em arquivos grandes
- Implementou um cache sob demanda de destaque por tokens
- O destaque é feito em chunks de tamanho aproximadamente igual
- Quando há uma modificação (damage) no buffer, apenas os chunks sobrepostos à posição alterada ou posteriores são invalidados
- Mesmo no pior caso (edição no meio de um arquivo grande), o estado de destaque antes da mudança é preservado, e o que está abaixo da área visível nem precisa ser processado, pois a informação não é requisitada
- Como é uma abordagem orientada por demanda, também funciona corretamente com múltiplos painéis exibindo partes diferentes do mesmo buffer
Busca no projeto inteiro
- O processo de busca tem 4 etapas
- Determinar a raiz do projeto procurando um diretório
.git/ ao subir a partir do diretório atual
- Percorrer recursivamente todos os diretórios da raiz e casar o padrão de busca com o conteúdo dos arquivos
- Para cada correspondência positiva, extrair um snippet do arquivo e aplicar destaque de sintaxe para a prévia do resultado
- Definir a ordem dos resultados segundo a distância de navegação em relação ao caminho atual (arquivos mais próximos recebem maior prioridade)
- Aplica regras padrão de filtragem para evitar diretórios de build e similares
- O processamento é multithread, com distribuição de trabalho entre threads em um esquema básico de work-stealing
- Resolveu o problema de detecção de término em uma estrutura especial em que todas as threads são consumidoras e produtoras
- Threads em espera incrementam um contador atômico; quando ele chega ao número de workers e a fila está vazia, o processamento termina globalmente
- Graças às otimizações de regex e à velocidade dos SSDs modernos, até em codebases grandes como Veloren a busca por padrões simples termina quase instantaneamente
- No flamegraph, o comportamento aparece majoritariamente como IO-bound
- Poder pesquisar grandes codebases na velocidade do pensamento dentro do editor contribui muito para a produtividade
Buffer do emulador de terminal
- Em um editor baseado em painéis, é muito conveniente poder usar um painel como janela de terminal
- Ele tentou implementar seu próprio parser ANSI, mas o suporte a recursos modernos de renderização de terminal, como OSC52 e a extensão de teclado Kitty, é vasto demais
- Então passou a usar o crate
alacritty_terminal para reutilizar o parser de sequências de escape e a lógica de gerenciamento de estado do emulador de terminal Alacritty
- Como resultado, consegue substituir recursos centrais de
screen/tmux e ainda oferecer suporte mais rico a sequências de escape
Otimização de renderização
- Mesmo sendo baseado em TUI, a largura de banda em conexões móveis remotas ainda é importante
- Double buffering: mantém duas cópias internas da tela do terminal
- Ao redesenhar, compara com o frame anterior e emite sequências de escape ANSI apenas para as células alteradas
- Sequências como movimento de cursor e mudança de modo de estilo também são emitidas só quando realmente necessário
- Na maioria dos emuladores de terminal (com exceção do Ghostty), abrir um arquivo grande com
cat no painel de terminal do editor e depois fechar o editor é mais rápido do que executar cat diretamente no terminal hospedeiro
- Isso porque
alacritty_terminal bloqueia o custo de processamento dos bytes enviados ao stdout do terminal hospedeiro
Conclusão: crie sua própria ferramenta
- O editor criado por ele se tornou uma ferramenta perfeitamente ajustada ao seu fluxo de trabalho
- Ele discorda da noção comum de que criar seu próprio editor ou ferramenta seja um sofrimento inútil
- Aponta quatro vantagens
- Personalização perfeita: faz exatamente o que ele quer, nem mais nem menos
- Aprendizado técnico amplo: adquiriu compreensão profunda de regex, ANSI, pseudoterminal (pty), design de TUI, detalhes do UTF-8 e outras habilidades úteis de forma geral
- Ganho de produtividade no longo prazo: por entender completamente sua ferramenta e embutir recursos ajustados ao fluxo pessoal, reduziu o atrito com as ferramentas
- Prazer genuíno: resolver problemas autocontidos e sentir o resultado na ponta dos dedos reacendeu seu amor pela programação; ele diz que voltou a codar sorrindo e até rindo sozinho depois de anos
- Mesmo que não seja um editor de texto, recomenda criar sua própria ferramenta e enfatiza a importância de aproveitar o desafio em si, sem terceirizar as partes difíceis para uma “caixa de estatística” (como IA)
5 comentários
Na verdade, a coisa mais surpreendente neste texto é uma palavra.
Fingerspitzengefühl
Finger (dedo) + Spitzen (ponta) + Gefühl (sensação)
O alemão realmente... nossa... até tem uma palavra para descrever a sensação tátil na ponta dos dedos ao manipular algo.
Então "Finger" também era alemão. Achei que fosse inglês...
Como pertencem ao mesmo grupo lexical, compartilham muito vocabulário básico.
É o alemão, em que as combinações de palavras podem ser infinitas, hehe.
Comentários do Hacker News
Foi um prazer ler do começo ao fim. Tenho até recomendado aos amigos que façam seu próprio editor de texto
Eu uso meu editor chamado “Left” há quase 10 anos. No começo ele não era perfeito, mas fui melhorando o Left editando o próprio Left com ele. A alegria que sinto toda manhã ao abrir uma ferramenta feita por mim compensa 20 vezes o tempo investido
Existe um ditado: “Na vida, você deve construir uma casa, plantar uma árvore e fazer um editor”. Eu comecei pela última parte
É uma frase do editor estilo Vi Vip, baseado em PicoLisp
Eu também fiz um editor de texto do zero. Como ele tem muitos recursos, usei bastante ferramentas externas como LSP, tree-sitter e fzf.
Projetei para que fosse possível customizá-lo no estilo suckless, apenas alterando o código de forma simples.
Nas primeiras semanas ele estava cheio de bugs, mas foi ficando cada vez mais estável à medida que eu corrigia. Dá para ver no meu projeto hat
Será que alguém pode recomendar uma boa biblioteca de edição de texto?
GUI é indispensável, então eu teria que lidar diretamente com renderizador de fonte e contexto gráfico.
Não me serve algo só para console, e se eu fizer só a GUI, não terei funcionalidade de edição, então preciso dos dois.
Surpreendentemente, é difícil encontrar uma biblioteca em forma de API pura que atenda a esses requisitos.
A maioria é um editor completo, ou algo no nível de um framework enorme.
Eu só queria um mecanismo básico de edição que conseguisse lidar rapidamente com arquivos de texto grandes
Com uma ferramenta como trolley, também dá para empacotar isso como uma UI nativa baseada em ghostty
Em vez disso, a combinação SDL com SDL_ttf é uma opção bem decente. O SDL3_ttf também melhorou no tratamento de strings
Reimplementei o editor “kilo”, do antirez.
O código original e o tutorial são muito bons, então foi um excelente projeto para aprender o básico de modo terminal e da linguagem C
Tenho lembranças de ter feito meu próprio editor nos anos 90 para arquivos COBOL e ASM.
Tinha realce de sintaxe, buffering rápido e até protetor de tela.
Rodava em um Pentium 120 e, na minha memória, era mil vezes mais rápido que o VSCode de hoje
Naquela época, a gente escrevia todas as tags HTML em maiúsculas
Este é o editor zte, feito pelo autor do texto
A frase “resista à tentação de empurrar as partes difíceis para dentro de uma caixa estatística” realmente me marcou
Eu também uso meu próprio editor. As outras pessoas não ligam muito, mas o valor que tiro de uma ferramenta feita por mim é enorme
Tem até uma função simples de “navegador” para abrir links com F5
Josh Barretto é o gênio que fez o port de Super Mario 64 para GBA. Eu certamente toparia experimentar um editor dele