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

12 KiB
Raw Permalink Blame History

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

Если какую-то команду выполнить нельзя, это нужно явно указать в ответе.