Skip to content

Commit aba3398

Browse files
create debug Neovim plugin for lsp-devtools and server logs (#292)
1 parent 6893a91 commit aba3398

File tree

1 file changed

+186
-1
lines changed

1 file changed

+186
-1
lines changed

.lazy.lua

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,189 @@ vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, {
1212
end,
1313
})
1414

15-
return {}
15+
local M = {}
16+
17+
M.state = {
18+
wins = {},
19+
bufs = {},
20+
timers = { devtools = nil, logs = nil },
21+
devtools = { job = nil, lines = {} },
22+
}
23+
24+
local function strip_ansi(str)
25+
return str:gsub("\27%[[0-9;]*m", "")
26+
end
27+
28+
local function create_buffer(opts)
29+
local buf = vim.api.nvim_create_buf(false, true)
30+
vim.api.nvim_set_current_buf(buf)
31+
vim.bo.modifiable = true
32+
vim.bo.buftype = "nofile"
33+
vim.bo.filetype = opts.filetype or "text"
34+
35+
local win = vim.api.nvim_get_current_win()
36+
vim.wo.number = opts.number ~= nil and opts.number or false
37+
vim.wo.relativenumber = false
38+
vim.wo.list = false
39+
vim.wo.wrap = opts.wrap or false
40+
41+
return buf, win
42+
end
43+
44+
local function auto_scroll(bufnr, line_count)
45+
local wins = vim.fn.win_findbuf(bufnr)
46+
for _, win in ipairs(wins) do
47+
vim.api.nvim_win_set_cursor(win, { line_count, 0 })
48+
end
49+
end
50+
51+
function M.ensure_devtools_running()
52+
if M.state.devtools.job then
53+
return
54+
end
55+
56+
M.state.devtools.lines = {}
57+
M.state.devtools.job = vim.fn.jobstart("just dev devtools record", {
58+
stdout_buffered = false,
59+
on_stdout = function(_, data)
60+
if not data then
61+
return
62+
end
63+
local cleaned = vim.tbl_map(strip_ansi, data)
64+
local filtered = vim.tbl_filter(function(line)
65+
return line ~= ""
66+
end, cleaned)
67+
for _, line in ipairs(filtered) do
68+
table.insert(M.state.devtools.lines, line)
69+
end
70+
end,
71+
on_exit = function()
72+
M.state.devtools.job = nil
73+
end,
74+
})
75+
end
76+
77+
function M.update_devtools(bufnr, start_idx)
78+
if not vim.api.nvim_buf_is_valid(bufnr) then
79+
return #M.state.devtools.lines
80+
end
81+
82+
local new_lines = vim.list_slice(M.state.devtools.lines, start_idx + 1)
83+
if #new_lines > 0 then
84+
vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, new_lines)
85+
auto_scroll(bufnr, vim.api.nvim_buf_line_count(bufnr))
86+
end
87+
88+
return #M.state.devtools.lines
89+
end
90+
91+
function M.update_logs(bufnr)
92+
local log_file = vim.fn.system("ls -t /tmp/djls.log.* 2>/dev/null | head -1"):gsub("%s+$", "")
93+
94+
if log_file == "" then
95+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "Waiting for server logs..." })
96+
return
97+
end
98+
99+
local lines = vim.fn.readfile(log_file)
100+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
101+
auto_scroll(bufnr, #lines)
102+
end
103+
104+
function M.close()
105+
for _, timer in pairs(M.state.timers) do
106+
if timer then
107+
timer:stop()
108+
end
109+
end
110+
M.state.timers = { devtools = nil, logs = nil }
111+
112+
for _, win in ipairs(M.state.wins) do
113+
if vim.api.nvim_win_is_valid(win) then
114+
vim.api.nvim_win_close(win, true)
115+
end
116+
end
117+
for _, buf in ipairs(M.state.bufs) do
118+
if vim.api.nvim_buf_is_valid(buf) then
119+
vim.api.nvim_buf_delete(buf, { force = true })
120+
end
121+
end
122+
123+
M.state.wins = {}
124+
M.state.bufs = {}
125+
end
126+
127+
function M.open()
128+
M.ensure_devtools_running()
129+
130+
local main_win = vim.api.nvim_get_current_win()
131+
132+
vim.cmd("vsplit")
133+
vim.cmd("wincmd l")
134+
vim.cmd("vertical resize 80")
135+
136+
-- Top: lsp-devtools
137+
local devtools_buf, devtools_win = create_buffer({ filetype = "json" })
138+
if #M.state.devtools.lines > 0 then
139+
vim.api.nvim_buf_set_lines(devtools_buf, 0, -1, false, M.state.devtools.lines)
140+
end
141+
142+
local last_line_count = #M.state.devtools.lines
143+
M.state.timers.devtools = vim.uv.new_timer()
144+
M.state.timers.devtools:start(
145+
100,
146+
100,
147+
vim.schedule_wrap(function()
148+
last_line_count = M.update_devtools(devtools_buf, last_line_count)
149+
end)
150+
)
151+
152+
table.insert(M.state.wins, devtools_win)
153+
table.insert(M.state.bufs, devtools_buf)
154+
155+
-- Bottom: server logs
156+
vim.cmd("split")
157+
vim.cmd("wincmd j")
158+
local log_buf, log_win = create_buffer({ filetype = "log", wrap = true })
159+
160+
M.update_logs(log_buf)
161+
M.state.timers.logs = vim.uv.new_timer()
162+
M.state.timers.logs:start(
163+
500,
164+
500,
165+
vim.schedule_wrap(function()
166+
M.update_logs(log_buf)
167+
end)
168+
)
169+
170+
table.insert(M.state.wins, log_win)
171+
table.insert(M.state.bufs, log_buf)
172+
173+
vim.api.nvim_set_current_win(main_win)
174+
end
175+
176+
function M.toggle()
177+
if #M.state.wins > 0 then
178+
M.close()
179+
else
180+
M.open()
181+
end
182+
end
183+
184+
vim.api.nvim_create_autocmd("VimLeavePre", {
185+
callback = function()
186+
if M.state.devtools.job then
187+
vim.fn.jobstop(M.state.devtools.job)
188+
end
189+
end,
190+
})
191+
192+
vim.api.nvim_create_user_command("DjlsDebugToggle", M.toggle, {})
193+
vim.keymap.set("n", "<leader>dd", M.toggle, { desc = "Toggle DJLS debug windows" })
194+
195+
return {
196+
{
197+
"fei6409/log-highlight.nvim",
198+
opts = {},
199+
},
200+
}

0 commit comments

Comments
 (0)