a6941b55c4
Implementado text_editor_cpp_core (PIMPL sobre ImGuiColorTextEdit MIT) y file_watcher_cpp_core (inotify Linux / ReadDirectoryChangesW Win) con demo combinada en primitives_gallery + smoke test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
151 lines
6.6 KiB
Markdown
151 lines
6.6 KiB
Markdown
# 0025 — C++ text_editor + file_watcher
|
|
|
|
## APP Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | 0025 |
|
|
| **Estado** | pendiente |
|
|
| **Prioridad** | alta |
|
|
| **Tipo** | feature — C++ devx (cpp/functions/core) |
|
|
|
|
## Dependencias
|
|
|
|
Ninguna. Se apoya en lo que ya existe (`tokens`, `app_base`, ImGui vendoreado).
|
|
|
|
**Desbloquea:** ciclo de edicion completo dentro de `shaders_lab` (editar GLSL + reload automatico al guardar) sin alt-tab a un editor externo. Tambien util para un futuro `sql_workbench` (issue 0032).
|
|
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
Añadir dos primitivos al registry C++:
|
|
|
|
1. **`text_editor_cpp_core`** — editor de codigo embebido en ImGui con syntax highlighting (GLSL, SQL, generic). Wrapper de [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) (header + cpp, MIT) vendoreado en `cpp/vendor/imgui_text_edit/`.
|
|
2. **`file_watcher_cpp_core`** — watcher de archivos cross-platform (inotify Linux / ReadDirectoryChangesW Win) puro en su API: registra paths + callback, expone `poll()` no bloqueante.
|
|
|
|
Mostrar ambos en `primitives_gallery` con una demo combinada: editor que carga un .glsl, file_watcher detecta cambios externos y el editor recarga.
|
|
|
|
## Contexto
|
|
|
|
`shaders_lab` actualmente compila un `gl_shader` desde texto en RAM. No hay editor in-app: el usuario edita en VSCode y copia/pega. Tampoco hay file_watcher: para iterar sobre `.glsl` externos hay que recompilar la app.
|
|
|
|
Otros usos del editor:
|
|
- `sql_workbench` (0032) — editor SQL + ejecucion sobre `registry.db`/`operations.db`.
|
|
- Edicion de prompts/configs en apps futuras.
|
|
|
|
## Arquitectura
|
|
|
|
```
|
|
cpp/
|
|
├── vendor/imgui_text_edit/ # NEW — vendor (MIT, 1 .h + 1 .cpp)
|
|
│ ├── TextEditor.h
|
|
│ └── TextEditor.cpp
|
|
├── functions/core/
|
|
│ ├── text_editor.h # NEW — wrapper en namespace fn
|
|
│ ├── text_editor.cpp # NEW
|
|
│ ├── text_editor.md # NEW
|
|
│ ├── file_watcher.h # NEW
|
|
│ ├── file_watcher.cpp # NEW (impure)
|
|
│ └── file_watcher.md # NEW
|
|
└── apps/primitives_gallery/
|
|
├── demos_text_editor.cpp # NEW — demo combinada (editor + watcher)
|
|
├── demos.h # MOD — declarar demo_text_editor()
|
|
├── main.cpp # MOD — registrar entrada en sidebar
|
|
└── CMakeLists.txt # MOD — añadir fuentes
|
|
cpp/CMakeLists.txt # MOD — añadir vendor/imgui_text_edit a sources
|
|
```
|
|
|
|
### Pure core / impure shell
|
|
|
|
- **`text_editor_cpp_core`**: API es **pura** en el sentido de "sin I/O propio" — todo el estado vive en una struct `TextEditorState`. La `TextEditor::Render()` hace draw calls de ImGui (igual que el resto de componentes). Marcar como `purity: pure`, `kind: component`. Misma categoria que `button_cpp_core`.
|
|
- **`file_watcher_cpp_core`**: hace I/O del sistema de ficheros. `purity: impure`, `kind: function`, `error_type: error_go_core` (usar el equivalente C++ — ver `tokens` para errores existentes; si no, retornar `bool` con `last_error()`).
|
|
|
|
### API propuesta
|
|
|
|
```cpp
|
|
namespace fn {
|
|
|
|
// text_editor.h
|
|
struct TextEditorState; // forward (PIMPL hacia TextEditor de vendor)
|
|
|
|
enum class CodeLang { Generic, GLSL, SQL, Cpp };
|
|
|
|
TextEditorState* text_editor_create(CodeLang lang = CodeLang::Generic);
|
|
void text_editor_destroy(TextEditorState*);
|
|
void text_editor_set_text(TextEditorState*, const char* text);
|
|
const char* text_editor_get_text(TextEditorState*); // valido hasta el siguiente call
|
|
bool text_editor_render(TextEditorState*, const char* label, ImVec2 size); // true si cambio
|
|
bool text_editor_is_dirty(const TextEditorState*);
|
|
void text_editor_clear_dirty(TextEditorState*);
|
|
|
|
// file_watcher.h
|
|
struct FileWatcher;
|
|
|
|
FileWatcher* file_watcher_create();
|
|
void file_watcher_destroy(FileWatcher*);
|
|
bool file_watcher_add(FileWatcher*, const char* path); // file or dir
|
|
struct FileEvent { std::string path; enum { Modified, Created, Deleted } kind; };
|
|
std::vector<FileEvent> file_watcher_poll(FileWatcher*); // non-blocking, drain
|
|
}
|
|
```
|
|
|
|
## Tareas
|
|
|
|
### Fase 1 — Vendor
|
|
|
|
- 1.1 Descargar [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) (commit estable, MIT) a `cpp/vendor/imgui_text_edit/`.
|
|
- 1.2 Añadirlo al `cpp/CMakeLists.txt` global como source list reusable.
|
|
|
|
### Fase 2 — text_editor
|
|
|
|
- 2.1 Implementar `text_editor.h/.cpp` con la API de arriba (PIMPL). Aplicar `tokens` para colores de fondo y border consistentes.
|
|
- 2.2 Configurar lenguajes: `LanguageDefinition::GLSL()` y `LanguageDefinition::SQL()` (vendor ya las trae).
|
|
- 2.3 `text_editor.md` con frontmatter completo (`kind: component`, `purity: pure`, `params`, `output`, ejemplo).
|
|
|
|
### Fase 3 — file_watcher
|
|
|
|
- 3.1 Implementar `file_watcher.h/.cpp`. Linux: inotify. Windows: ReadDirectoryChangesW (solo dir-level, filtrar por path). Macros condicionales.
|
|
- 3.2 `file_watcher.md` (`kind: function`, `purity: impure`).
|
|
|
|
### Fase 4 — Gallery demo
|
|
|
|
- 4.1 `demos_text_editor.cpp` con `demo_text_editor()`: split horizontal — izquierda editor con un GLSL de ejemplo, derecha info (dirty flag, eventos del watcher). Botón "Save to /tmp/demo.glsl" + watcher activo sobre `/tmp/demo.glsl` que muestra los eventos.
|
|
- 4.2 Registrar en `demos.h`, `main.cpp`, `CMakeLists.txt`.
|
|
|
|
### Fase 5 — Integracion en shaders_lab (opcional)
|
|
|
|
- 5.1 (Stretch) Añadir flag `--watch <archivo.glsl>` a `shaders_lab` que abre el archivo con text_editor + file_watcher y recompila automaticamente.
|
|
|
|
### Fase 6 — Tests + docs
|
|
|
|
- 6.1 Tests para `file_watcher` (Linux): crear tmpfile, modify, verificar evento. Compilable solo en Linux con guard.
|
|
- 6.2 `./fn index` y `./fn show text_editor_cpp_core` / `file_watcher_cpp_core` verifica frontmatter.
|
|
|
|
## Ejemplo de uso
|
|
|
|
```cpp
|
|
auto* ed = fn::text_editor_create(fn::CodeLang::GLSL);
|
|
auto* fw = fn::file_watcher_create();
|
|
fn::file_watcher_add(fw, "/tmp/demo.glsl");
|
|
|
|
fn::run_app("editor demo", [&]{
|
|
if (fn::text_editor_render(ed, "##ed", {600, 400}))
|
|
std::printf("changed\n");
|
|
for (auto& ev : fn::file_watcher_poll(fw))
|
|
std::printf("FS: %s\n", ev.path.c_str());
|
|
});
|
|
```
|
|
|
|
## Decisiones de diseño
|
|
|
|
- **PIMPL** evita exponer `TextEditor` del vendor en headers publicos.
|
|
- **Vendor in-tree** sigue la convencion de ImGui/ImPlot ya presentes en `cpp/vendor/`.
|
|
- **Sin tokens custom** — heredamos los del color theme global.
|
|
|
|
## Riesgos
|
|
|
|
- **API del vendor cambia entre commits**: pinear a un commit concreto en un README en `cpp/vendor/imgui_text_edit/`.
|
|
- **inotify watch limit en Linux**: documentar `fs.inotify.max_user_watches` en el .md.
|
|
- **Windows ReadDirectoryChangesW** solo emite a nivel de directorio: filtrar el path en el poll.
|