Files
fn_registry/dev/issues/completed/0025-cpp-text-editor-file-watcher.md

6.8 KiB

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0025 C++ text_editor + file_watcher completado feature
cpp-stack
multi-app alta
2026-05-17 2026-05-17

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 (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

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 (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).
  • 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

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.