- Debido a las limitaciones del editor Howl existente (desarrollo descontinuado, búsqueda lenta, incompatibilidad con SSH y falta de soporte de terminal), desarrolló directamente un nuevo editor de texto TUI
- Probó 13 editores, incluidos Helix, VS Code, Vim, Neovim y Emacs, pero ninguno satisfizo la sensación de manejo (Fingerspitzengefühl) que buscaba
- Al inicio implementó solo un conjunto mínimo de funciones personalizadas, dejando para después el rendimiento, Unicode y el soporte multilingüe, y luego fue ampliándolo gradualmente
- Durante el desarrollo implementó por su cuenta un motor de expresiones regulares, explorador de archivos, renderizado basado en TUI e integración con buffer de terminal, entre otros
- Aplicó múltiples técnicas de optimización de rendimiento en la búsqueda global del proyecto, resaltado de sintaxis, manejo de caché y distribución de trabajo multihilo
- Como resultado, terminó una herramienta perfectamente adaptada a su propio flujo de trabajo y afirma que recuperó tanto la productividad como el placer de programar
Limitaciones de los editores existentes y búsqueda de alternativas
- Los problemas del editor Howl, que usó durante unos 10 años, fueron lo que lo llevó a desarrollarlo por su cuenta
- El desarrollo estuvo detenido durante años, así que mantuvo su propio fork, pero al estar escrito en MoonScript era difícil hacer cambios profundos
- El rendimiento de la búsqueda de archivos en todo el proyecto era insuficiente y eso interrumpía el flujo de trabajo
- Al ser un editor GUI, no podía usarse de forma remota mediante conexión SSH
- Como no tenía terminal integrada, no era posible interactuar en vivo al ejecutar comandos externos, y la mayoría de los códigos de escape ANSI no eran compatibles
- Probó 13 editores, entre ellos Helix, VS Code, Sublime Text, Vim, Zed, Neovim, Emacs, Geany, Micro, Lite XL, Lapce, GNOME Builder y Kakoune
- Cada uno tenía ventajas, pero ninguno cumplía con la sensación de manejo (Fingerspitzengefühl) que quería
- El que usó por más tiempo fue Helix, pero perdió el interés después de un mes
Estrategia inicial de desarrollo
- En la etapa inicial limitó el alcance al mínimo
- Excluyó funciones pensadas para otros usuarios y dejó toda la configuración hardcodeada
- Posponiendo la optimización de rendimiento, comenzó con un buffer basado en
String
- Excluyó el soporte completo para grafemas Unicode y consideró suficiente que el símbolo
£ ocupara una sola columna
- Para el resaltado de sintaxis, solo dio soporte a unos pocos lenguajes que usa con frecuencia, y para el resto lo sustituyó con un resaltado genérico basado en delimitadores
- En el segundo intento primero construyó un sencillo framework TUI, pero con el tiempo eliminó la mayor parte y cambió a un enfoque más directo y detallado
Dogfooding en la práctica
- Una vez que el editor alcanzó el umbral mínimo de funcionalidad para abrir, editar y guardar un solo archivo, comenzó tres prácticas
- Usarlo a la fuerza en lugar de
nano para editar archivos del sistema o tomar notas
- Registrar en el
README.md del proyecto cada función faltante, bug, comportamiento extraño o limitación que encontrara
- Corregir de inmediato los problemas lo bastante molestos
- Estas tres prácticas hicieron que la carga de trabajo pasara de 1 hora al mes a varias horas por semana
- De unas 10,000 líneas de código en total, casi todas se escribieron en los últimos 6 meses
Manejo del cursor
- El manejo del cursor es un área difícil de implementar
- Combinaciones de teclas como
ctrl + shift + left son obvias para el usuario, pero la lógica para implementarlas es compleja
- El consejo clave es implementar la entrada de alto nivel como una combinación de operaciones primitivas
- Ejemplo: retroceso por palabra → descomponer en mover el cursor por palabra + seleccionar rango + eliminar
- Al implementar undo/redo, hay que agrupar estas 3 acciones para garantizar un resultado intuitivo
- Esto le hizo entender por qué los editores modales exponen directamente estas operaciones primitivas al usuario
Explorador de archivos
- El explorador de archivos de Howl fue la razón decisiva por la que no pudo pasarse a otros editores
- Su filtro difuso con actualización inmediata era excelente, y en la mayoría de los casos bastaban 1 o 2 teclas para encontrar el archivo deseado
- Si el archivo no existía, podía crearse en línea
- Al escribir
~/, cambiaba automáticamente al directorio personal
- Mostraba una vista previa del archivo que iba a abrirse desde la ventana principal de edición
- Le molestaba que otros editores resolvieran el problema de abrir archivos con dependencia del mouse, diálogos predeterminados de GTK o adivinación del nombre del archivo
- En su implementación propia, en lugar de métodos complejos como Levenshtein distance, encontró que bastaban tres criterios simples
- Si el nombre empieza con el texto del filtro
- Si el nombre contiene el texto del filtro
- La fecha de modificación/acceso más reciente
- Permitió coincidencias ignorando mayúsculas y minúsculas, pero subió ligeramente el ranking cuando sí coincidían exactamente
- Incluso en proyectos con decenas de miles de archivos, después de 2 teclas el archivo deseado queda entre los 2 primeros con una probabilidad de aproximadamente 95%
Motor de expresiones regulares
- Las expresiones regulares se usan en tres lugares: búsqueda global del proyecto, resaltado de sintaxis y búsqueda dentro del buffer
- Razones para implementarlo por cuenta propia en lugar de usar el crate
regex-automata existente
- Necesitaba manejar casos límite sensibles al contexto, como la sintaxis de raw strings de Rust
- El proyecto en sí era un ejercicio para construir y entender su propio stack
- La implementación inicial analizaba la sintaxis de regex con el crate de parsing
chumsky y recorría el AST carácter por carácter, por lo que era lenta
- Después aplicó optimizaciones por etapas
- Optimizador de una sola pasada: convierte grupos repetidos de coincidencia de caracteres en un único nodo
String para hacer búsqueda exacta de cadenas
- Extracción de prefijos comunes: por ejemplo, al encontrar el prefijo común
hel en hel[(lo)p], hace la coincidencia solo en esas posiciones → gran mejora de rendimiento en la búsqueda global del proyecto
- Reimplementó el walker del AST como una VM de threaded code basada en llamadas dinámicas de Rust
- Transformó esa VM de threaded code a una forma CPS (Continuation-Passing Style), donde cada instrucción de la VM hace tail-call a la siguiente para aprovechar las optimizaciones del compilador
- Encapsuló las lentas llamadas dinámicas de Rust sin lookup de vtable, reduciendo la generación de código de muchas instrucciones regex a unas pocas instrucciones de máquina
- Implementó tantas instrucciones regex como fue posible a nivel de bytes en vez de puntos de código Unicode; gracias al diseño de UTF-8, las técnicas de optimización para ASCII también sirven para puntos de código multibyte
- También intentó compilar a cadenas de jump LUT, pero los benchmarks mostraron que solo era 20–30% más rápido que threaded code y reducía mucho la flexibilidad, así que no lo adoptó
- Resultado final: incluso en el resaltado de sintaxis más complejo para Rust, logra resaltar por completo un archivo de bindings autogenerado de 50,000 líneas en menos de 10 milisegundos desde un estado limpio
Caché de resaltado de sintaxis
- Al principio volvía a resaltar todo el archivo con cada cambio, pero eso degradaba el rendimiento en archivos grandes
- Implementó una caché de resaltado de tokens bajo demanda
- Resalta tokens en chunks de tamaño aproximadamente uniforme
- Cuando hay cambios (damage) en el buffer, solo invalida los chunks que se superponen con esa posición o vienen después
- Incluso en el peor caso (editar en medio de un archivo grande), el estado de resaltado previo al damage se conserva, y no hace falta procesar la parte inferior de la pantalla porque la información de resaltado no se solicita
- Al ser un enfoque guiado por demanda (demand-driven), también funciona bien con múltiples paneles viendo distintas partes del mismo buffer
Búsqueda global del proyecto
- El proceso de búsqueda tiene 4 etapas
- Determinar la raíz del proyecto buscando hacia atrás un directorio
.git/ desde el directorio actual
- Recorrer recursivamente todos los directorios de la raíz del proyecto y hacer coincidir el patrón de búsqueda con el contenido de los archivos
- Extraer un fragmento del archivo de cada coincidencia positiva y aplicar resaltado de sintaxis para la vista previa de resultados
- Ordenar los resultados según la distancia de navegación desde la ruta actual (los archivos más cercanos tienen mayor prioridad)
- Aplica reglas de filtrado predeterminadas para evitar directorios de build y similares
- Usa procesamiento multihilo, con una forma básica de work-stealing para asignar tareas entre hilos
- Resolvió el problema de detección de terminación en una estructura especial donde todos los hilos son a la vez productores y consumidores
- Los hilos en espera incrementan un contador atómico y, cuando el contador alcanza la cantidad de workers y la cola de tareas está vacía, todo termina
- Gracias a la optimización de regex y a la velocidad de los SSD modernos, incluso en un codebase grande como Veloren, las búsquedas de patrones simples terminan casi al instante
- En el flamegraph, la mayor parte del tiempo está limitado por IO
- Poder buscar dentro de un codebase grande a la velocidad del pensamiento desde el editor aporta mucho a la productividad
Buffer del emulador de terminal
- En un editor basado en paneles, es muy útil poder usar uno de ellos como ventana de terminal
- Intentó implementar por su cuenta un parser ANSI, pero el soporte para funciones modernas de renderizado de terminal, como OSC52 y la extensión de teclado de Kitty, es enorme
- Aprovechó el crate
alacritty_terminal para reutilizar el parser de secuencias de escape y la lógica de manejo de estado de terminal del emulador Alacritty
- Como resultado, puede sustituir las funciones clave de
screen/tmux y además ofrece un soporte más rico de secuencias de escape
Optimización de renderizado
- Aunque está basado en TUI, el ancho de banda sigue siendo importante cuando se usa con conexiones móviles remotas
- Doble buffering: mantiene una copia interna doble de la pantalla del terminal
- Al redibujar, compara con el frame anterior y solo emite secuencias de escape ANSI para las celdas modificadas
- Las secuencias para mover el cursor o cambiar de modo de estilo también se emiten solo cuando realmente hacen falta
- En la mayoría de los emuladores de terminal (excepto Ghostty), hacer
cat de un archivo grande en el panel de terminal del editor y luego cerrar el editor es más rápido que hacer cat directamente en la terminal del host
- Porque
alacritty_terminal bloquea el costo de procesar los bytes de stdout hacia la terminal del host
Conclusión: crea tu propia herramienta
- El editor hecho por él mismo se convirtió en una herramienta perfectamente adaptada a su flujo de trabajo
- Se opone a la idea común de que crear tu propio editor o herramienta es un sufrimiento inútil
- Cuatro ventajas
- Personalización perfecta: hace exactamente lo que quiere, ni más ni menos
- Aprendizaje de tecnologías diversas: obtuvo una comprensión profunda de tecnologías útiles de forma general, como regex, ANSI, pseudoterminales (pty), diseño TUI y detalles de UTF-8
- Mejora de productividad a largo plazo: al entender perfectamente su herramienta e incorporar funciones adaptadas a su flujo personal, reduce la fricción con la herramienta
- Puro disfrute: resolver problemas autocontenidos y sentir el resultado en las yemas de los dedos reavivó su amor por la programación; cuenta que volvió a sonreír programando y a reírse solo mientras codificaba por primera vez en años
- Aunque no sea un editor de texto, recomienda crear tu propia herramienta, y enfatiza disfrutar el reto en sí en lugar de delegar las partes difíciles a una caja estadística (como la IA)
5 comentarios
La verdad, lo más sorprendente de este texto es una sola palabra.
Fingerspitzengefühl
Finger(dedo) + Spitzen(punta) + Gefühl(sensación)
El alemán de verdad tiene una palabra para la sensación de control en la punta de los dedos... ah...
Vaya,
Fingertambién era alemán. Pensé que era inglés...Como pertenecen al mismo grupo lingüístico, comparten mucho vocabulario básico.
Es alemán, donde las combinaciones de palabras pueden ser infinitas jaja
Comentarios en Hacker News
Me la pasé muy bien leyéndolo de principio a fin. Incluso les recomiendo a mis amigos que intenten hacer su propio editor de texto
Llevo casi 10 años usando mi editor, llamado ‘Left’. Al principio no era perfecto, pero lo he ido mejorando editando Left con Left. La alegría que siento cada mañana al abrir una herramienta hecha por mí compensa 20 veces ese tiempo invertido
Hay un dicho que dice: “En la vida hay que construir una casa, plantar un árbol y crear un editor”. Yo empecé por lo último
Es una frase que aparece en Vip, un editor estilo Vi basado en PicoLisp
Yo también hice un editor de texto desde cero. Como tenía muchas funciones, aproveché bastante herramientas externas como LSP, tree-sitter y fzf.
Lo diseñé al estilo suckless, para que se pudiera personalizar fácilmente solo modificando el código.
Las primeras semanas estaba lleno de bugs, pero mientras más lo corregía, más estable se volvía. Pueden ver mi proyecto hat
¿Alguien podría recomendar una buena biblioteca de edición de texto?
La GUI es indispensable, así que también hay que lidiar con el renderizado de fuentes y el contexto gráfico.
Algo solo para consola no me sirve, pero si solo hago la GUI entonces no tengo funciones de edición, así que necesito ambas cosas.
Sorprendentemente, es difícil encontrar una biblioteca en forma de API pura que cumpla con esos requisitos.
La mayoría son editores ya terminados o frameworks enormes.
Me bastaría con un motor básico de edición que simplemente pueda manejar archivos de texto grandes con rapidez
Incluso se puede envolver como una UI nativa basada en ghostty con herramientas como trolley
En cambio, la combinación de SDL y SDL_ttf es bastante buena. SDL3_ttf también mejora el manejo de cadenas
Volví a implementar el editor “kilo” de antirez.
El código original y el tutorial están muy bien hechos, así que fue un proyecto excelente para aprender lo básico del modo terminal y del lenguaje C
Tengo recuerdos de haber hecho mi propio editor en los 90 para archivos COBOL y ASM.
Tenía resaltado de sintaxis, buffering rápido e incluso salvapantallas.
Corría en un Pentium 120 y, según recuerdo, era mil veces más rápido que el VSCode actual
En esa época escribíamos todas las etiquetas HTML en mayúsculas
Este es el editor zte que hizo el autor del artículo
Me impactó mucho la frase: “Resiste la tentación de empujar las partes difíciles a una caja estadística”
Yo también uso mi propio editor. A los demás no les interesa mucho, pero hay mucho valor en usar una herramienta hecha por uno mismo
Incluso tiene una función simple de ‘navegador’ para abrir enlaces con F5
Josh Barretto es un genio que hizo el port de Super Mario 64 para GBA. Si es su editor, con gusto lo probaría