Files
qode/AGENTS.md
T
2026-11-01 01:54:53 +03:00

152 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<RefCell<_>>` / `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
```
Если какую-то команду выполнить нельзя, это нужно явно указать в ответе.