11 KiB
11 KiB
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и уже подключённых крейтов. - Изменения должны быть локальными и в стиле окружающего кода. Не добавляйте параллельные пути управления и не оставляйте мёртвый код.
Проверка
Перед завершением изменения:
cargo fmt
cargo test
cargo build
Если какую-то команду выполнить нельзя, это нужно явно указать в ответе.