refactoring
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
-- Стартовый сценарий приложения.
|
||||
-- Стартовый сценарий приложения qode.
|
||||
--
|
||||
-- Приложение выполняет этот файл при запуске. Каталог поиска задаётся
|
||||
-- константой SCRIPT_DIR в src/constants.rs (по умолчанию — текущий каталог),
|
||||
-- но его можно переопределить аргументом командной строки:
|
||||
--
|
||||
-- gpui-editor-test --config /path/to/project main.go
|
||||
-- qode --config /path/to/project main.go
|
||||
-- cargo run -- --config ./config
|
||||
-- cargo run -- main.go
|
||||
--
|
||||
@@ -21,8 +21,12 @@ print("[lua] init.lua выполнен")
|
||||
-- app:next_tab() -- переключить на следующую, вернуть индекс
|
||||
-- app:previous_tab() -- переключить на предыдущую, вернуть индекс
|
||||
-- app:open_tab() -- открыть пустую вкладку, вернуть индекс
|
||||
-- app:open(path) -- открыть файл с диска, вернуть индекс вкладки;
|
||||
-- app:open(path) -- открыть файл с диска, вернуть индекс вкладки;
|
||||
-- -- если файла нет — пустой буфер с таким именем
|
||||
-- app:open(path, {}) -- открыть и перевести фокус в редактор,
|
||||
-- -- курсор оставить на месте
|
||||
-- app:open(path, {m, n}) -- открыть, перевести фокус и поставить курсор
|
||||
-- -- на строку m и столбец n (с нуля)
|
||||
-- app:list_dir(path) -- {{ id = "src/main.rs", name = "main.rs",
|
||||
-- -- is_dir = false }, ...}; пустой путь —
|
||||
-- -- текущий каталог, id — путь от него
|
||||
@@ -39,17 +43,28 @@ print("[lua] init.lua выполнен")
|
||||
-- app:language(i) -- язык подсветки вкладки или nil
|
||||
-- app:clear_language(i) -- выключить подсветку для вкладки
|
||||
-- app:save() -- сохранить активную вкладку в её файл
|
||||
-- app:map_key(keys, fn) -- обработчик клавиши/сочетания
|
||||
-- app:map_key(keys, handler) -- обработчик клавиши/сочетания:
|
||||
-- -- функция, имя команды или nil (удалить)
|
||||
-- -- "a" нажатие A
|
||||
-- -- "1" нажатие 1
|
||||
-- -- "!a" отпускание A
|
||||
-- -- "<S-c>" C при зажатом Shift
|
||||
-- -- "<C-z>" Z при зажатом Ctrl
|
||||
-- -- "<A-!r>" отпускание R при зажатом Alt
|
||||
-- -- "<D-s>" S при Cmd/Super
|
||||
-- -- команды: save, next_tab, previous_tab,
|
||||
-- -- open_tab, close_tab, toggle_left_panel,
|
||||
-- -- toggle_right_panel, toggle_bottom_panel
|
||||
-- -- клавиша сравнивается с именем клавиши от GPUI:
|
||||
-- -- Caps Lock не важен; на Linux и Windows буквы
|
||||
-- -- берутся по аппаратному keycode, поэтому
|
||||
-- -- <C-q> работает и на русской раскладке
|
||||
-- app:map_completion_key(keys, handler)
|
||||
-- -- то же для списка автодополнения; команды:
|
||||
-- -- completion_trigger, completion_next,
|
||||
-- -- completion_previous, completion_page_up,
|
||||
-- -- completion_page_down, completion_accept,
|
||||
-- -- completion_cancel, completion_dismiss
|
||||
-- app:close() -- закрыть окно
|
||||
-- app:toggle_maximize()
|
||||
-- app:set_maximized(true/false)
|
||||
@@ -70,12 +85,32 @@ print("[lua] init.lua выполнен")
|
||||
-- app:lsp_servers() -- { "gopls", ... }
|
||||
-- app:stop_lsp("gopls") -- true, если сервер был запущен
|
||||
-- app:restart_lsp("gopls") -- перезапуск с прежними настройками
|
||||
-- app:on_lsp_diagnostics(delay_ms, fn)
|
||||
-- -- fn(servers) вызывается один раз
|
||||
-- -- после затишья delay_ms; nil вместо
|
||||
-- -- функции удаляет обработчик.
|
||||
-- -- servers =
|
||||
-- -- { { server = "gopls",
|
||||
-- -- uri = "file:///...",
|
||||
-- -- path = "/...",
|
||||
-- -- diagnostics = {
|
||||
-- -- { line = 0, character = 2,
|
||||
-- -- end_line = 0, end_character = 8,
|
||||
-- -- severity = 1,
|
||||
-- -- severity_name = "error",
|
||||
-- -- message = "...",
|
||||
-- -- source = "gopls",
|
||||
-- -- code = "...",
|
||||
-- -- tags = { "unnecessary" } },
|
||||
-- -- } }, ... }
|
||||
-- -- Позиции нумеруются с нуля, как в LSP.
|
||||
--
|
||||
-- Поле watcher принимает строку или таблицу:
|
||||
-- Поле watcher принимает строку, таблицу или boolean:
|
||||
-- watcher = "**/*.go"
|
||||
-- watcher = { pattern = "**/*.go", ignore = { "vendor/**" } }
|
||||
-- watcher = true
|
||||
--
|
||||
-- Автодополнение включается само при вводе идентификатора; Ctrl+Space
|
||||
-- Автодополнение включается само при вводе идентификатора; Ctrl/Cmd+Space
|
||||
-- открывает список вручную. Пока список открыт, ↑/↓ и PageUp/PageDown
|
||||
-- перемещают выделение, Enter или Tab вставляют элемент, Esc закрывает
|
||||
-- список; горизонтальные стрелки закрывают его и двигают курсор.
|
||||
@@ -103,9 +138,13 @@ print("[lua] init.lua выполнен")
|
||||
-- Блоки панелей. Сценарий сам решает, что показывает блок: редактор умеет
|
||||
-- рисовать только абстрактное дерево.
|
||||
-- local tree = app.left_panel:add_tree()
|
||||
-- local tab = app.bottom_panel:add_tree("Диагностика")
|
||||
-- -- title показывается вкладкой в нижней
|
||||
-- -- панели; боковые панели его не рисуют
|
||||
-- tree:append(nodes, parent_id) -- nodes = { { id=..., label=..., icon=...,
|
||||
-- -- expandable=..., expanded=... }, ... };
|
||||
-- -- parent_id = "" — корень дерева
|
||||
-- tree:clear() -- удалить все узлы и сбросить выбор
|
||||
-- tree:remove(id) -- удалить ноду и всех потомков
|
||||
-- tree:on_toggle(fn) -- fn(expanded, id, loaded)
|
||||
-- tree:on_click(fn) -- fn(id) — клик по конечной ноде
|
||||
@@ -122,9 +161,6 @@ print("[lua] init.lua выполнен")
|
||||
-- Подсветка синтаксиса по умолчанию выключена. Включить её можно так:
|
||||
-- app:set_language(0, "rust")
|
||||
|
||||
app:set_language(0, "rust")
|
||||
app:next_tab()
|
||||
|
||||
-- gopls запускается автоматически при старте редактора.
|
||||
-- Сервер обслуживает только документы с languageId "go" и получает изменения
|
||||
-- всех .go-файлов проекта (кроме vendor/) через файловый вотчер.
|
||||
@@ -155,6 +191,10 @@ end
|
||||
-- Пример привязки: Ctrl+S сохраняет вкладку, отпускание Esc закрывает окно.
|
||||
-- app:map_key("<C-s>", function() app:save() end)
|
||||
-- app:map_key("<!Esc>", function() app:close() end)
|
||||
-- Убрать встроенную привязку можно так:
|
||||
-- app:map_key("<C-s>", nil)
|
||||
-- app:map_completion_key("<C-n>", "completion_next")
|
||||
-- app:map_completion_key("<C-p>", "completion_previous")
|
||||
|
||||
-- Файловое дерево проекта.
|
||||
--
|
||||
@@ -185,7 +225,8 @@ project_tree:on_toggle(function(expanded, id, loaded)
|
||||
end)
|
||||
|
||||
project_tree:on_click(function(id)
|
||||
app:open(id)
|
||||
-- Пустая таблица: фокус уходит в редактор, курсор остаётся на месте.
|
||||
app:open(id, {})
|
||||
end)
|
||||
|
||||
app.left_panel:open()
|
||||
@@ -205,7 +246,140 @@ 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:map_key("<C-e>", "toggle_left_panel")
|
||||
app:map_key("<C-t>", "toggle_bottom_panel")
|
||||
|
||||
app:open_tab()
|
||||
-- Дерево диагностики в нижней панели.
|
||||
--
|
||||
-- Обработчик один на все LSP-серверы. Второй аргумент регистрации — пауза
|
||||
-- тишины: если серверы присылают диагностику чаще, промежуточные вызовы
|
||||
-- пропускаются, а последняя остаётся и обрабатывается после затишья.
|
||||
-- При каждом вызове дерево очищается целиком и строится заново.
|
||||
local diagnostics_tree = app.bottom_panel:add_tree("Диагностика")
|
||||
|
||||
local severity_icons = {
|
||||
error = "circle-x",
|
||||
warning = "triangle-alert",
|
||||
information = "circle-alert",
|
||||
hint = "lightbulb",
|
||||
}
|
||||
|
||||
local severity_rank = {
|
||||
error = 1,
|
||||
warning = 2,
|
||||
information = 3,
|
||||
hint = 4,
|
||||
}
|
||||
|
||||
local function diagnostic_rank(diagnostic)
|
||||
return diagnostic.severity
|
||||
or severity_rank[diagnostic.severity_name or "error"]
|
||||
or 1
|
||||
end
|
||||
|
||||
app:on_lsp_diagnostics(1000, function(servers)
|
||||
diagnostics_tree:clear()
|
||||
|
||||
-- Сначала собираем диагностику по файлам: одинаковые проблемы от разных
|
||||
-- серверов попадают в одну запись.
|
||||
local files = {}
|
||||
local file_order = {}
|
||||
for _, entry in ipairs(servers) do
|
||||
local path = entry.path or entry.uri
|
||||
local file = files[path]
|
||||
if not file then
|
||||
file = { diagnostics = {} }
|
||||
files[path] = file
|
||||
file_order[#file_order + 1] = path
|
||||
end
|
||||
for _, diagnostic in ipairs(entry.diagnostics) do
|
||||
file.diagnostics[#file.diagnostics + 1] = diagnostic
|
||||
end
|
||||
end
|
||||
|
||||
for _, path in ipairs(file_order) do
|
||||
local file = files[path]
|
||||
|
||||
-- Ключ группировки — начало и конец диапазона: у таких проблем
|
||||
-- совпадают и позиция, и длина.
|
||||
local groups = {}
|
||||
local group_order = {}
|
||||
for _, diagnostic in ipairs(file.diagnostics) do
|
||||
local key = string.format(
|
||||
"%d:%d:%d:%d",
|
||||
diagnostic.line,
|
||||
diagnostic.character,
|
||||
diagnostic.end_line,
|
||||
diagnostic.end_character
|
||||
)
|
||||
local group = groups[key]
|
||||
if not group then
|
||||
group = {
|
||||
line = diagnostic.line,
|
||||
character = diagnostic.character,
|
||||
rank = diagnostic_rank(diagnostic),
|
||||
severity = diagnostic.severity_name or "error",
|
||||
messages = {},
|
||||
seen = {},
|
||||
}
|
||||
groups[key] = group
|
||||
group_order[#group_order + 1] = key
|
||||
else
|
||||
local rank = diagnostic_rank(diagnostic)
|
||||
if rank < group.rank then
|
||||
group.rank = rank
|
||||
group.severity = diagnostic.severity_name or "error"
|
||||
end
|
||||
end
|
||||
if not group.seen[diagnostic.message] then
|
||||
group.seen[diagnostic.message] = true
|
||||
group.messages[#group.messages + 1] = diagnostic.message
|
||||
end
|
||||
end
|
||||
|
||||
local errors = {}
|
||||
for index, key in ipairs(group_order) do
|
||||
local group = groups[key]
|
||||
errors[#errors + 1] = {
|
||||
-- Префикс отделяет позицию от id файла, даже если в пути есть ':'.
|
||||
id = string.format(
|
||||
"diag:%s:%d:%d:%d",
|
||||
path,
|
||||
group.line,
|
||||
group.character,
|
||||
index
|
||||
),
|
||||
label = string.format(
|
||||
"%d:%d %s",
|
||||
group.line + 1,
|
||||
group.character + 1,
|
||||
table.concat(group.messages, "; ")
|
||||
),
|
||||
icon = severity_icons[group.severity] or "circle-alert",
|
||||
}
|
||||
end
|
||||
|
||||
diagnostics_tree:append({
|
||||
{
|
||||
id = path,
|
||||
label = path,
|
||||
icon = "file",
|
||||
expandable = true,
|
||||
expanded = true,
|
||||
},
|
||||
}, "")
|
||||
diagnostics_tree:append(errors, path)
|
||||
end
|
||||
end)
|
||||
|
||||
diagnostics_tree:on_click(function(id)
|
||||
local path, line, character = id:match("^diag:(.*):(%d+):(%d+):%d+$")
|
||||
if path then
|
||||
app:open(path, { tonumber(line), tonumber(character) })
|
||||
else
|
||||
-- Клик по файлу: фокус в редактор, курсор не переставляем.
|
||||
app:open(id, {})
|
||||
end
|
||||
end)
|
||||
|
||||
app.bottom_panel:open()
|
||||
|
||||
Reference in New Issue
Block a user