212 lines
12 KiB
Lua
212 lines
12 KiB
Lua
-- Стартовый сценарий приложения.
|
||
--
|
||
-- Приложение выполняет этот файл при запуске. Каталог поиска задаётся
|
||
-- константой SCRIPT_DIR в src/constants.rs (по умолчанию — текущий каталог),
|
||
-- но его можно переопределить аргументом командной строки:
|
||
--
|
||
-- gpui-editor-test --config /path/to/project main.go
|
||
-- cargo run -- --config ./config
|
||
-- cargo run -- main.go
|
||
--
|
||
-- В каталоге --config должны лежать init.lua и подключаемые им модули.
|
||
-- Первый позиционный аргумент открывается после выполнения этого файла
|
||
-- через app:open(path) — как `vim main.go` или `nano main.go`.
|
||
|
||
print("[lua] init.lua выполнен")
|
||
|
||
-- Доступные методы объекта app (номера вкладок — с нуля):
|
||
-- app:tabs() -- {{ index = 0, title = "main.rs", icon = "file" }, ...}
|
||
-- app:active_tab() -- индекс активной вкладки
|
||
-- app:set_active_tab(index) -- true, если вкладка существует
|
||
-- app:next_tab() -- переключить на следующую, вернуть индекс
|
||
-- app:previous_tab() -- переключить на предыдущую, вернуть индекс
|
||
-- app:open_tab() -- открыть пустую вкладку, вернуть индекс
|
||
-- app:open(path) -- открыть файл с диска, вернуть индекс вкладки;
|
||
-- -- если файла нет — пустой буфер с таким именем
|
||
-- app:list_dir(path) -- {{ id = "src/main.rs", name = "main.rs",
|
||
-- -- is_dir = false }, ...}; пустой путь —
|
||
-- -- текущий каталог, id — путь от него
|
||
-- app:close_tab(index) -- true, если вкладка закрыта
|
||
-- app:set_tab_icon(i, name) -- иконка вкладки из каталога Lucide
|
||
-- -- (file, file-code, book-open, ...);
|
||
-- -- false — нет вкладки или иконки
|
||
-- app:clear_tab_icon(i) -- вернуть иконку по умолчанию (file)
|
||
-- app:tab_icon(i) -- имя иконки вкладки или nil
|
||
-- app:active_text() -- текст активной вкладки
|
||
-- app:set_active_text(text) -- заменить текст активной вкладки
|
||
-- app:set_language(i, "rust") -- включить подсветку (rust|go|js|json|python)
|
||
-- -- для вкладки i; false — нет вкладки/языка
|
||
-- app:language(i) -- язык подсветки вкладки или nil
|
||
-- app:clear_language(i) -- выключить подсветку для вкладки
|
||
-- app:save() -- сохранить активную вкладку в её файл
|
||
-- app:map_key(keys, fn) -- обработчик клавиши/сочетания
|
||
-- -- "a" нажатие A
|
||
-- -- "1" нажатие 1
|
||
-- -- "!a" отпускание A
|
||
-- -- "<S-c>" C при зажатом Shift
|
||
-- -- "<C-z>" Z при зажатом Ctrl
|
||
-- -- "<A-!r>" отпускание R при зажатом Alt
|
||
-- -- клавиша сравнивается с именем клавиши от GPUI:
|
||
-- -- Caps Lock не важен; на Linux и Windows буквы
|
||
-- -- берутся по аппаратному keycode, поэтому
|
||
-- -- <C-q> работает и на русской раскладке
|
||
-- app:close() -- закрыть окно
|
||
-- app:toggle_maximize()
|
||
-- app:set_maximized(true/false)
|
||
-- app:minimize()
|
||
-- app:is_maximized()
|
||
--
|
||
-- LSP-серверы (запуск целиком настраивается этим сценарием):
|
||
-- app:run_lsp({
|
||
-- name = "gopls", -- условное имя для stop/restart
|
||
-- cmd = "gopls", -- путь к бинарнику или имя в PATH
|
||
-- args = { "serve" }, -- аргументы процесса
|
||
-- env = { GOFLAGS = "-mod=mod" }, -- переменные окружения
|
||
-- languages = { "go" }, -- какие languageId обслуживает
|
||
-- watcher = "**/*.go", -- шаблон файлового вотчера
|
||
-- root = ".", -- корень workspace (по умолчанию cwd)
|
||
-- initialization_options = {}, -- initializationOptions сервера
|
||
-- })
|
||
-- app:lsp_servers() -- { "gopls", ... }
|
||
-- app:stop_lsp("gopls") -- true, если сервер был запущен
|
||
-- app:restart_lsp("gopls") -- перезапуск с прежними настройками
|
||
--
|
||
-- Поле watcher принимает строку или таблицу:
|
||
-- watcher = "**/*.go"
|
||
-- watcher = { pattern = "**/*.go", ignore = { "vendor/**" } }
|
||
--
|
||
-- Автодополнение включается само при вводе идентификатора; Ctrl+Space
|
||
-- открывает список вручную. Пока список открыт, ↑/↓ и PageUp/PageDown
|
||
-- перемещают выделение, Enter или Tab вставляют элемент, Esc закрывает
|
||
-- список; горизонтальные стрелки закрывают его и двигают курсор.
|
||
-- Документация выбранного элемента показывается второй всплывающей панелью
|
||
-- справа от списка. Примеры запуска:
|
||
-- app:run_lsp({ name = "gopls", cmd = "gopls", args = { "serve" },
|
||
-- languages = { "go" }, watcher = "**/*.go" })
|
||
-- app:run_lsp({ name = "rust-analyzer", cmd = "rust-analyzer",
|
||
-- languages = { "rust" }, watcher = "**/*.rs" })
|
||
--
|
||
-- Панели окна (по умолчанию закрыты, размер меняется перетаскиванием):
|
||
-- app.left_panel:open()
|
||
-- app.left_panel:close()
|
||
-- app.left_panel:toggle() -- вернуть true, если панель открыта
|
||
-- app.left_panel:is_open()
|
||
-- app.right_panel:open() -- те же методы, что у левой панели
|
||
-- app.right_panel:close()
|
||
-- app.right_panel:toggle()
|
||
-- app.right_panel:is_open()
|
||
-- app.bottom_panel:open() -- те же методы, что у левой панели
|
||
-- app.bottom_panel:close()
|
||
-- app.bottom_panel:toggle()
|
||
-- app.bottom_panel:is_open()
|
||
--
|
||
-- Блоки панелей. Сценарий сам решает, что показывает блок: редактор умеет
|
||
-- рисовать только абстрактное дерево.
|
||
-- local tree = app.left_panel:add_tree()
|
||
-- tree:append(nodes, parent_id) -- nodes = { { id=..., label=..., icon=...,
|
||
-- -- expandable=..., expanded=... }, ... };
|
||
-- -- parent_id = "" — корень дерева
|
||
-- tree:remove(id) -- удалить ноду и всех потомков
|
||
-- tree:on_toggle(fn) -- fn(expanded, id, loaded)
|
||
-- tree:on_click(fn) -- fn(id) — клик по конечной ноде
|
||
-- tree:selected() -- id выбранной строки или nil
|
||
-- tree:focus(id) -- раскрыть родителей, выбрать и дать фокус
|
||
--
|
||
-- Методы добавляются в src/api.rs: всё, что есть в impl AppApi, помеченном
|
||
-- #[mlua::userdata_impl], автоматически доступно из Lua.
|
||
--
|
||
-- Другие Lua-файлы можно подключать из каталога сценариев:
|
||
-- include("tools.lua") -- выполнить файл сразу
|
||
-- require("tools") -- выполнить один раз (как обычный Lua-модуль)
|
||
--
|
||
-- Подсветка синтаксиса по умолчанию выключена. Включить её можно так:
|
||
-- app:set_language(0, "rust")
|
||
|
||
app:set_language(0, "rust")
|
||
app:next_tab()
|
||
|
||
-- gopls запускается автоматически при старте редактора.
|
||
-- Сервер обслуживает только документы с languageId "go" и получает изменения
|
||
-- всех .go-файлов проекта (кроме vendor/) через файловый вотчер.
|
||
local gopls_ok, gopls_err = pcall(function()
|
||
app:run_lsp({
|
||
name = "gopls",
|
||
cmd = "gopls",
|
||
args = { "serve" },
|
||
-- Рабочий каталог gopls. --config меняет только каталог init.lua,
|
||
-- поэтому проект должен быть текущим каталогом запуска.
|
||
root = ".",
|
||
languages = { "go" },
|
||
watcher = {
|
||
pattern = "**/*.go",
|
||
ignore = { "vendor/**" },
|
||
},
|
||
-- gopls читает эти параметры из initializationOptions.
|
||
initialization_options = {
|
||
usePlaceholders = true,
|
||
completeUnimported = true,
|
||
},
|
||
})
|
||
end)
|
||
if not gopls_ok then
|
||
print("[lsp] gopls не запущен: " .. tostring(gopls_err))
|
||
end
|
||
|
||
-- Пример привязки: Ctrl+S сохраняет вкладку, отпускание Esc закрывает окно.
|
||
-- app:map_key("<C-s>", function() app:save() end)
|
||
-- app:map_key("<!Esc>", function() app:close() end)
|
||
|
||
-- Файловое дерево проекта.
|
||
--
|
||
-- Редактор рисует абстрактное дерево; смысл узлов (файлы, папки, что угодно)
|
||
-- задаёт этот сценарий. Здесь корень — текущий каталог, а каждая папка
|
||
-- дозагружается при первом раскрытии.
|
||
local project_tree = app.left_panel:add_tree()
|
||
|
||
local function load_dir(tree, path)
|
||
local nodes = {}
|
||
for _, entry in ipairs(app:list_dir(path)) do
|
||
nodes[#nodes + 1] = {
|
||
id = entry.id,
|
||
label = entry.name,
|
||
icon = entry.is_dir and "folder" or "file",
|
||
expandable = entry.is_dir,
|
||
expanded = false,
|
||
}
|
||
end
|
||
tree:append(nodes, path)
|
||
end
|
||
|
||
project_tree:on_toggle(function(expanded, id, loaded)
|
||
-- loaded == false только при первом раскрытии: позже дети уже в дереве.
|
||
if expanded and not loaded then
|
||
load_dir(project_tree, id)
|
||
end
|
||
end)
|
||
|
||
project_tree:on_click(function(id)
|
||
app:open(id)
|
||
end)
|
||
|
||
app.left_panel:open()
|
||
load_dir(project_tree, "")
|
||
|
||
-- Открытие/закрытие панели с деревом. При открытии возвращаем фокус на
|
||
-- последнюю выбранную строку, чтобы можно было сразу продолжить навигацию.
|
||
app:map_key("<C-b>", function()
|
||
local opened = app.left_panel:toggle()
|
||
if opened then
|
||
local selected = project_tree:selected()
|
||
if selected then
|
||
project_tree:focus(selected)
|
||
end
|
||
end
|
||
end)
|
||
|
||
app:map_key("<C-q>", function() app:close() end)
|
||
|
||
app:map_key("<C-e>", function() app.left_panel:toggle() end)
|
||
app:map_key("<C-t>", function() app.bottom_panel:toggle() end)
|
||
|
||
app:open_tab()
|