100 lines
5.6 KiB
Markdown
100 lines
5.6 KiB
Markdown
# qode — конструктор IDE на Lua
|
||
|
||
`qode` — настольный редактор кода на GPUI (`gpui-kit`), который расширяется
|
||
Lua-сценариями. При запуске приложение выполняет `init.lua` и предоставляет
|
||
сценарию объект `api`.
|
||
|
||
## Идея
|
||
|
||
Rust-часть даёт только базовые универсальные примитивы:
|
||
|
||
- окно и его элементы;
|
||
- вкладки, текстовый редактор, подсветка, gutter, автодополнение;
|
||
- панели и абстрактное дерево;
|
||
- плавающее поле ввода и список выбора;
|
||
- файловая система;
|
||
- привязки клавиш;
|
||
- LSP-клиент по stdio.
|
||
|
||
Вся бизнес-логика IDE — файловое дерево, дерево диагностики, команды,
|
||
раскладки, проектные сценарии, оформление и поведение — живёт в Lua.
|
||
Если поведение зависит от проекта или рабочего процесса, его место в
|
||
`init.lua` или подключаемых модулях, а не в Rust.
|
||
|
||
Новую возможность в Rust стоит добавлять, только если это универсальный
|
||
примитив, нужный разным сценариям, или если её нельзя выразить через уже
|
||
существующее API (доступ к GPUI, ОС, протоколу LSP). Бизнес-правила
|
||
(какие файлы показывать, как создавать документы, что писать в статус,
|
||
какие команды запускать) остаются в Lua.
|
||
|
||
## Запуск и каталог сценариев
|
||
|
||
Каталог поиска `init.lua` задаётся константой `SCRIPT_DIR` в
|
||
`src/constants.rs` (по умолчанию — текущий каталог). Его можно переопределить
|
||
аргументом командной строки:
|
||
|
||
```sh
|
||
qode --config /path/to/project main.go
|
||
cargo run -- --config ./config
|
||
cargo run -- main.go
|
||
```
|
||
|
||
- В каталоге `--config` должны лежать `init.lua` и подключаемые им модули.
|
||
- Первый позиционный аргумент открывается после выполнения `init.lua`
|
||
через `api.editor:open(path)` — как `vim main.go` или `nano main.go`.
|
||
- `--config` меняет только каталог сценариев. Рабочий каталог процесса, а
|
||
вместе с ним пути `api.editor:open`, `api.editor:save`, `api.path:*` и
|
||
`root` у LSP не меняются.
|
||
|
||
## Объект `api`
|
||
|
||
Объект `api` разбит на группы. Методы вызываются через двоеточие:
|
||
|
||
```lua
|
||
api.editor:open("src/main.rs")
|
||
api.editor:save()
|
||
api.panels.left:open()
|
||
api.path:list("src")
|
||
api.window:close()
|
||
api.keymap:map_key("", "<C-s>", "save")
|
||
```
|
||
|
||
| Группа | Назначение | Документация |
|
||
| --- | --- | --- |
|
||
| `api.editor` | Вкладки, текст, gutter, подсветка, сохранение, LSP, привязки редактора | [editor.md](editor.md) |
|
||
| `api.panels` | Левая, правая и нижняя панели, верхняя панель редактора, блоки-деревья | [panels.md](panels.md) |
|
||
| `api.path` | Файловая система: чтение, запись, создание и удаление | [path.md](path.md) |
|
||
| `api.window` | Окно, плавающее поле ввода и список выбора | [window.md](window.md) |
|
||
| `api.keymap` | Глобальные привязки клавиш и режимы | [keymap.md](keymap.md) |
|
||
| `api.editor` (LSP) | Запуск серверов и обработка диагностики | [lsp.md](lsp.md) |
|
||
|
||
Номера вкладок во всех методах нумеруются с нуля. Массив, который возвращает
|
||
`api.editor:tabs()`, остаётся обычной Lua-таблицей с индексами от 1, поэтому
|
||
поле `index` — единственный надёжный способ сослаться на вкладку.
|
||
|
||
## Подключение других Lua-файлов
|
||
|
||
Файлы подключаются из каталога сценариев:
|
||
|
||
| Способ | Описание |
|
||
| --- | --- |
|
||
| `include("tools.lua")` | Выполнить файл сразу |
|
||
| `require("tools")` | Выполнить один раз (как обычный Lua-модуль) |
|
||
|
||
## Добавление новых методов
|
||
|
||
Методы добавляются в `src/api.rs`. Публичные методы `impl`-блока с
|
||
`#[mlua::userdata_impl]` автоматически доступны из Lua:
|
||
|
||
- методы `AppApi` — у объекта `api`;
|
||
- методы `EditorApi` — у `api.editor`;
|
||
- методы `PanelApi` — у `api.panels.left`, `.right`, `.bottom`;
|
||
- методы `ToolbarApi` — у `api.panels.toolbar`;
|
||
- методы `PathApi` — у `api.path`;
|
||
- методы `WindowApi` — у `api.window`;
|
||
- методы `KeymapApi` — у `api.keymap`.
|
||
|
||
Чтобы не экспортировать метод, его помечают `#[lua(skip)]`.
|
||
Новый вид блока панели должен добавить методы в свой `*Api` и отдавать свою
|
||
`KeyMap`, как это делает `TreeApi`.
|