Estado actual: Fase 2 completada ✅ Objetivo: Agregar funcionalidades avanzadas tipo Vim
- BufferManager con init y guards
- FasAPI con Builder pattern
- EditorState completamente implementado
- HookRegistry arreglado con FasEventType
- FasKeymap type-safe
- TerminalInput con raw mode
- Key enum completo
- Cursor con movimientos Vim (hjkl, w, b, e, 0, $, g, G)
- Buffer operations (insert, delete, getText)
- UndoManager funcional
- UIRenderer con viewport y scroll
- Comandos básicos (:w, :q, :e, :wq)
- 4 modos implementados (normal, insert, visual, command)
- Visual mode con yank/delete
- Argumentos de línea de comandos
Objetivo: Soportar 5j, 3w, 10x, 2dd
Implementación:
- Agregar campo
counta EditorState - Parser de números en normal mode
- Aplicar count a movimientos y comandos
Ejemplo de uso:
5j → Bajar 5 líneas
3w → Avanzar 3 palabras
10x → Eliminar 10 caracteres
2dd → Eliminar 2 líneas
Dificultad: ⭐⭐ (Media) Tiempo estimado: 2-3 horas
Objetivo: Búsqueda incremental con /pattern y ?pattern
Implementación:
- Agregar modo de búsqueda (search mode)
- Buffer de búsqueda para pattern
- Función de búsqueda en Buffer
- Highlight de matches
- Navegación con n/N
Ejemplo de uso:
/hello → Buscar "hello" hacia adelante
?world → Buscar "world" hacia atrás
n → Siguiente match
N → Match anterior
Componentes:
// SearchEngine.swift
public struct SearchEngine {
public func search(
pattern: String,
in buffer: Buffer,
from: BufferPosition,
direction: SearchDirection
) -> [BufferPosition]
public enum SearchDirection {
case forward
case backward
}
}Dificultad: ⭐⭐⭐ (Media-Alta) Tiempo estimado: 4-6 horas
Objetivo: Pegar texto yankeado con p (después) y P (antes)
Implementación:
- Verificar que ya tenemos registers en EditorState
- Implementar comando
pen normal mode - Implementar comando
Pen normal mode - Manejar paste de líneas completas vs caracteres
Ejemplo de uso:
yy → Yank línea actual
p → Paste después del cursor
P → Paste antes del cursor
Dificultad: ⭐⭐ (Media) Tiempo estimado: 2-3 horas
Objetivo: Combinar operators (d, y, c) con motions (w, $, 2j)
Implementación:
- Parser de comandos compuestos
- Sistema de "pending operator"
- Aplicar operator al motion
Ejemplos:
d2w → Delete 2 words
y$ → Yank hasta fin de línea
c3w → Change 3 words
diw → Delete inner word
Componentes:
// Operator.swift
public enum Operator {
case delete
case yank
case change
case visual
}
// Motion.swift
public enum Motion {
case words(count: Int)
case toEndOfLine
case toStartOfLine
case lines(count: Int)
}
// OperatorMotionParser.swift
public struct OperatorMotionParser {
public func parse(_ input: String) -> (Operator, Motion)?
}Dificultad: ⭐⭐⭐⭐ (Alta) Tiempo estimado: 6-8 horas
Objetivo: Marcar posiciones y saltar a ellas
Implementación:
- Agregar
marks: [Character: BufferPosition]a EditorState - Comando
m{a-z}para marcar - Comando
'{a-z}para saltar
Ejemplo de uso:
ma → Marcar posición como 'a'
'a → Saltar a marca 'a'
`` → Saltar a posición anterior
Dificultad: ⭐⭐ (Media) Tiempo estimado: 2-3 horas
Objetivo: Registros nombrados y especiales
Implementación:
- Registros nombrados:
"aa"z - Registro sin nombre:
"" - Registro de sistema (clipboard):
"+ - Registros de búsqueda:
"/
Ejemplo de uso:
"ayy → Yank línea al registro 'a'
"ap → Paste desde registro 'a'
"+yy → Yank al clipboard del sistema
"+p → Paste desde clipboard
Componentes:
// RegisterManager.swift
public final class RegisterManager {
private var registers: [String: String] = [:]
public var unnamed: String {
get { registers[""] ?? "" }
set { registers[""] = newValue }
}
public var systemClipboard: String {
get { NSPasteboard.general.string(forType: .string) ?? "" }
set { NSPasteboard.general.setString(newValue, forType: .string) }
}
public subscript(name: String) -> String {
get { registers[name] ?? "" }
set { registers[name] = newValue }
}
}Dificultad: ⭐⭐⭐ (Media-Alta) Tiempo estimado: 3-4 horas
Objetivo: Grabar y reproducir secuencias de comandos
Implementación:
- Estado de grabación en EditorState
- Buffer de macro
- Grabar teclas durante
q{a-z} - Reproducir con
@{a-z} - Repetir última macro con
@@
Ejemplo de uso:
qa → Empezar a grabar en registro 'a'
... → Ejecutar comandos
q → Detener grabación
@a → Ejecutar macro 'a'
@@ → Repetir última macro
3@a → Ejecutar macro 'a' 3 veces
Componentes:
// MacroRecorder.swift
public final class MacroRecorder {
private var isRecording: Bool = false
private var currentRegister: Character?
private var recordedKeys: [Key] = []
public func startRecording(register: Character)
public func stopRecording()
public func recordKey(_ key: Key)
public func playback(register: Character) -> [Key]
}Dificultad: ⭐⭐⭐⭐ (Alta) Tiempo estimado: 5-7 horas
- Día 1-2: Implementar Counts (5j, 3w)
- Día 3-4: Implementar Paste (p, P)
- Día 5: Implementar Marks (m, ')
- Día 1-3: Implementar Búsqueda (/, ?, n, N)
- Día 4-5: Implementar Sistema de Registros
- Día 1-3: Implementar Operators + Motions (d2w, y$)
- Día 4-5: Implementar Macros (q, @)
- Testing exhaustivo de todas las features
- Bug fixes y refinamiento
- Documentación de uso
Al completar la Fase 3, el editor debería poder:
✅ Repetir comandos con counts (5j, 3w)
✅ Buscar texto con / y ?
✅ Navegar búsqueda con n y N
✅ Pegar texto con p y P
✅ Combinar operators y motions (d2w, y$)
✅ Marcar posiciones y saltar (ma, 'a)
✅ Usar registros nombrados ("ayy, "ap)
✅ Copiar/pegar con clipboard del sistema ("+y, "+p)
✅ Grabar y ejecutar macros (qa, @a)
- Todos los counts funcionan (1-999)
- Búsqueda encuentra todos los matches
- Paste funciona con líneas y caracteres
- Operators + motions son composables
- Marks persisten durante la sesión
- Registros mantienen contenido
- Macros se pueden grabar y reproducir
- Búsqueda < 100ms en archivos de 10,000 líneas
- Paste instantáneo (< 50ms)
- Macros ejecutan sin lag visible
- Feedback visual claro (modo, count, pending operator)
- Mensajes de error descriptivos
- Comportamiento idéntico a Vim en casos comunes
Sources/FasCore/
├── Search/
│ ├── SearchEngine.swift # NEW
│ └── SearchMode.swift # NEW
├── Operators/
│ ├── Operator.swift # NEW
│ ├── Motion.swift # NEW
│ └── OperatorMotionParser.swift # NEW
├── Registers/
│ └── RegisterManager.swift # NEW
├── Macros/
│ └── MacroRecorder.swift # NEW
└── State/
└── EditorState.swift # MODIFICAR (agregar fields)
:help count:help /:help registers:help recording
- Neovim:
/src/nvim/search.c - Kakoune:
/src/registers.cc - Helix:
/helix-term/src/keymap.rs
- Reset count después de ejecutar comando
- Validar que count > 0
- Manejar overflow (count > 10000)
- Escapar caracteres especiales en regex
- Wrap around al final del archivo
- Highlight todos los matches
- Detectar si es paste de línea o carácter
- Ajustar cursor después de paste
- Registrar en undo
- Parser robusto que no confunda comandos
- Timeout si usuario no completa el comando
- Cancelar con ESC
- No grabar comandos que entran/salen de macro mode
- Límite de recursión para evitar loops infinitos
- Guardar macros en archivo para persistencia
Recomendación: Empezar con Paste (p y P) porque:
- Ya tenemos registers implementados
- Ya tenemos yank funcionando
- Es relativamente simple
- Da satisfacción inmediata (feature muy usada)
¿Quieres que empecemos con Paste?
Documento creado: 2025-10-10 Fase actual: Iniciando Fase 3 Próxima actualización: Después de completar primera feature