# AGENTS.md `qode` — настольный редактор кода на GPUI (`gpui-kit`) с расширением поведения через Lua и клиентом LSP по stdio. Этот файл описывает архитектуру и принципы, которые нужно сохранять при изменениях. ## Модули | Путь | Ответственность | | --- | --- | | `src/main.rs` | Точка входа, окно, аргументы командной строки | | `src/constants.rs` | Константы окна, CLI и каталог сценариев | | `src/editor.rs` | Доменная модель: документы (`Rope`), вкладки, языки | | `src/api.rs` | `AppApi` / `EditorApi` / `PanelsApi` / `PanelApi` / `ToolbarApi` / `TreeApi` / `PathApi` / `WindowApi` / `KeymapApi` — единственная поверхность управления | | `src/script.rs` | Lua-движок, `api`, `include`, `require` | | `src/prompt.rs` | Плавающее поле ввода: запрос из Lua и открытое окно | | `src/keymap.rs` | `KeyBinding`, `KeyMap`, встроенные команды и Lua-обработчики | | `src/app.rs` | GPUI-вид, отрисовка, события, автодополнение | | `src/completion.rs` | Всплывающий список, `CaretPopup`, преобразования LSP-элементов | | `src/panel.rs` | `PanelState` и блоки панелей | | `src/toolbar.rs` | Состояние верхней панели: лейблы и кнопки | | `src/tree.rs` | Абстрактное дерево для панелей | | `src/gutter.rs` | Иконки на полях слева от редактора и их сдвиг по `InputEdit` | | `src/icons.rs` | Каталог иконок Lucide | | `src/lsp.rs` | Менеджер LSP-серверов, JSON-RPC, файловый вотчер | | `docs/*.md`, `init.lua` | Пользовательская документация и пример конфигурации | ## Главная идея: конструктор IDE qode — не готовый редактор с фиксированным набором функций, а конструктор своей IDE. Rust/GPUI даёт универсальные примитивы, Lua задаёт поведение: - **Rust** отвечает за окно, вкладки, текстовый редактор, подсветку, gutter, автодополнение, панели, абстрактное дерево, плавающее поле ввода, доступ к файловой системе, клавиатуру и LSP-клиент. - **Lua** отвечает за всё, что делает редактор именно этой IDE: файловое дерево, дерево диагностики, команды, раскладки, статусы, проектные сценарии, оформление панелей и последовательности действий. Правило для изменений: - В Rust добавляется только универсальный примитив, которым могут пользоваться разные сценарии, или то, что нельзя выразить через уже существующее API (доступ к GPUI, ОС, протоколу LSP). - Если поведение выражается через API из Lua, оно должно жить в Lua, а не в Rust: никаких проектных правил, готовых панелей с бизнес-данными, демонстрационных виджетов и команд под конкретный workflow. - Новый примитив сразу получает Lua-метод и описание в `docs/`. ## Главные принципы ### 1. `AppApi` — центр приложения - Документы, вкладки, панели, дерево, окно, подсветка, LSP и привязки клавиш меняются только через `AppApi`, `EditorApi`, `PanelsApi`, `PanelApi`, `ToolbarApi`, `TreeApi`, `PathApi`, `WindowApi` или `KeymapApi`. - Всё, что относится к редактору (вкладки, текст, gutter, сохранение, LSP и привязки редактора), живёт в `api.editor`; панели — в `api.panels`, файловая система — в `api.path`, окно — в `api.window`, глобальные привязки — в `api.keymap`. - Состояние живёт в общих `Rc>` / `Arc`, чтобы UI, API и Lua видели одни и те же объекты. Второй копии состояния быть не должно. - Команды окна складываются в очередь и применяются в начале кадра (`apply_window_commands`); UI и Lua не вызывают `Window` напрямую. - Если действие пользователя меняет состояние, оно выражается методом API, а не логикой внутри обработчика UI. Отрисовка состояние только читает. - Исключение — системные кнопки окна внутри `gpui-kit::TitleBar`: их поведение принадлежит библиотеке. ### 2. Lua повторяет возможности API - Публичные методы `impl AppApi` / `impl EditorApi` / `impl PathApi` и других `*Api` с `#[mlua::userdata_impl]` доступны из Lua через объект `api`: `api.editor:method(...)`, `api.path:method(...)`, `api.panels.left:method(...)`. Добавляя возможность, сразу добавляйте и Lua-метод. - Имена — `snake_case`; вкладки и индексы нумеруются с нуля; панели — `api.panels.left`, `api.panels.right`, `api.panels.bottom`, `api.panels.toolbar`; блоки панелей получают свои объекты (`tree`). - `AppApi` — только группа-навигатор: методы живут в `EditorApi`, `PanelsApi`, `PathApi`, `WindowApi` или `KeymapApi`. Не добавляйте новый метод прямо в `api`, если для него есть подходящая группа. - Все методы `PathApi` принимают путь либо строкой `"foo/bar"`, либо массивом сегментов `{ "foo", "bar" }`. - Для методов, которые не должны попадать в Lua, используется `#[lua(skip)]`. Для generic-методов добавляется тонкая не-generic Lua-обёртка. - `docs/*.md` и комментарии в `init.lua` обновляются в том же изменении, что и публичный API. Документация описывает фактическое поведение, без «запланированных» методов. ### 3. Клавиатура — только через `KeyMap` - Встроенные привязки (`Ctrl/Cmd+S`, `Ctrl/Cmd+Space`, навигация по списку автодополнения) регистрируются в `KeyMap` и могут быть заменены или удалены из Lua через `api.keymap:map_key` / `api.editor:map_key` / `api.keymap:map_completion_key`. - У приложения, редактора и каждого блока панели своя `KeyMap` и свой текущий режим (один символ; `-` — режим по умолчанию). Привязка хранит строку режимов, в которых она действует: `""` — все, `"-iv"` — по умолчанию и `i`/`v`. - Глобальные привязки обрабатываются всегда; затем, если фокус в редакторе, дополнительно привязки редактора, а если фокус на блоке панели — привязки этого блока. Обработчик может быть Lua-функцией или встроенной командой. - Перехватчик клавиш выполняется раньше действий редактора и используется только для слоя автодополнения. Этот порядок менять нельзя. - Текстовый ввод редактора — не привязка клавиши. ### 4. Модель отделена от GPUI - `editor.rs`, `panel.rs` и `tree.rs` не знают о `Window` и `Context`. - Вид переносит изменения модели в виджет по `Workspace::revision`; набор текста в редакторе ревизию не трогает, чтобы не сбрасывать курсор. - Инвариант `Workspace`: открыт хотя бы один документ. Поэтому `active()` возвращает документ без `Option`. - Встроенный старт — одна пустая вкладка. Примеры и демонстрационные документы не должны попадать в код приложения; `init.lua` — единственное место для примеров. ### 5. LSP работает в фоне - `LspManager` общий (`Arc`) для вида, API и фоновых задач. - Процессы, чтение stdout/stderr и файловый вотчер живут в отдельных потоках; ответы приходят через каналы и ожидаются в `background_executor`, а не в кадре UI. - Какие серверы запускать, решает Lua; менеджер отвечает только за жизненный цикл, JSON-RPC и синхронизацию документов. - Текст документа хранится как `Rope`, поэтому правка из редактора копируется за O(1). LSP получает инкрементальный `didChange` с диапазонами из `InputEdit`; полный текст материализуется только для серверов без инкрементальной синхронизации и на границах (сохранение, Lua). ## Соглашения - Код, комментарии и пользовательская документация — на русском. - Публичные элементы Rust документируются `///`. - Новые зависимости добавляются только при реальной необходимости; сначала используются возможности `gpui-kit`, `mlua` и уже подключённых крейтов. - Изменения должны быть локальными и в стиле окружающего кода. Не добавляйте параллельные пути управления и не оставляйте мёртвый код. ## Проверка Перед завершением изменения: ```sh cargo fmt cargo test cargo build ``` Если какую-то команду выполнить нельзя, это нужно явно указать в ответе.