Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions lua/quicktest/colored_printer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,7 @@ local function get_highlight_def(group)
return hl_string
end

function ColoredPrinter:set_next_lines(lines, buf, shift)
local line_count = vim.api.nvim_buf_line_count(buf)

function ColoredPrinter:set_next_lines(lines, buf, lines_count)
local parsed_lines = {}
local parsed_highlights = {}

Expand All @@ -245,13 +243,13 @@ function ColoredPrinter:set_next_lines(lines, buf, shift)
table.insert(parsed_highlights, highlights)
end

api.nvim_buf_set_lines(buf, line_count - shift, -1, false, parsed_lines)
api.nvim_buf_set_lines(buf, lines_count, -1, false, parsed_lines)

for i, hl in ipairs(parsed_highlights) do
for _, h in ipairs(hl) do
-- print("[" .. h.start .. "," .. h.end_ .. ")", get_highlight_def(h.group))

api.nvim_buf_add_highlight(buf, -1, h.group, line_count - shift - 1 + i, h.start, h.end_)
api.nvim_buf_add_highlight(buf, -1, h.group, lines_count - 1 + i, h.start, h.end_)
end
end

Expand Down
223 changes: 137 additions & 86 deletions lua/quicktest/module.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ local M = {}
---@field default_win_mode WinModeWithoutAuto
---@field use_builtin_colorizer boolean

--- @type {id: number, started_at: number, pid: number?} | nil
---@alias JobStatus 'running' | 'finished' | 'canceled'
---@alias CmdJob {id: number, started_at: number, finished_at?: number, pid: number?, status: JobStatus, exit_code?: number}
---@type CmdJob | nil
local current_job = nil
--- @type {[string]: {type: string, adapter_name: string, bufname: string, cursor_pos: integer[]}} | nil
local previous_run = nil
Expand Down Expand Up @@ -120,6 +122,9 @@ local function get_adapter_by_name(adapters, name)
return adapter
end

local status_ns = vim.api.nvim_create_namespace("quicktest_status")
local stderr_ns = vim.api.nvim_create_namespace("quicktest_stderr")

--- @param adapter QuicktestAdapter
--- @param params any
--- @param config QuicktestConfig
Expand Down Expand Up @@ -149,42 +154,61 @@ function M.run(adapter, params, config, opts)

local printer = colorized_printer.new()

--- @type {id: number, started_at: number, pid: number?, exit_code: number?}
local job = { id = math.random(10000000000000000), started_at = vim.uv.now() }
--- @type CmdJob
local job = { id = math.random(10000000000000000), started_at = vim.uv.now(), status = "running" }
current_job = job

local is_running = function()
return current_job and job.id == current_job.id
return current_job and job.id == current_job.id and current_job.status == "running"
end

local print_status = function()
for _, buf in ipairs(ui.get_buffers()) do
local line_count = vim.api.nvim_buf_line_count(buf)
local print_buf_status = function(buf, line_count)
local passedTime = vim.loop.now() - job.started_at
if job.finished_at then
passedTime = job.finished_at - job.started_at
end

local passedTime = vim.loop.now() - job.started_at
local time_display = string.format("%.2f", passedTime / 1000) .. "s"
local time_display = string.format("%.2f", passedTime / 1000) .. "s"

if job.exit_code == nil then
vim.api.nvim_buf_set_lines(buf, line_count - 1, line_count, false, {
"Running " .. time_display,
})
vim.api.nvim_buf_add_highlight(buf, -1, "DiagnosticInfo", line_count - 1, 0, -1)
vim.api.nvim_buf_clear_namespace(buf, status_ns, 0, -1)

local hl_group = ""
local line = ""
if job.exit_code == nil and job.status == "running" then
line = "Running " .. time_display
hl_group = "DiagnosticInfo"
else
if job.status == "canceled" then
line = "Canceled " .. time_display
hl_group = "DiagnosticWarn"
elseif job.exit_code ~= 0 then
line = "Failed " .. time_display
hl_group = "DiagnosticError"
else
if job.exit_code ~= 0 then
vim.api.nvim_buf_set_lines(buf, line_count - 1, line_count, false, { "Failed " .. time_display })
line = "Passed " .. time_display
hl_group = "DiagnosticOk"
end
end

vim.api.nvim_buf_add_highlight(buf, -1, "DiagnosticError", line_count - 1, 0, -1)
else
vim.api.nvim_buf_set_lines(buf, line_count - 1, line_count, false, { "Passed " .. time_display })
vim.api.nvim_buf_set_lines(buf, line_count - 1, line_count, false, {
line,
})
vim.api.nvim_buf_set_extmark(buf, status_ns, line_count - 1, 0, {
end_col = line:len(),
hl_group = hl_group,
})
end
local print_status = function()
for _, buf in ipairs(ui.get_buffers()) do
local line_count = vim.api.nvim_buf_line_count(buf)

vim.api.nvim_buf_add_highlight(buf, -1, "DiagnosticOk", line_count - 1, 0, -1)
end
end
print_buf_status(buf, line_count)
end
end

for _, buf in ipairs(ui.get_buffers()) do
vim.api.nvim_buf_set_lines(buf, 0, -1, false, {})
vim.api.nvim_buf_clear_namespace(buf, -1, 0, -1)

ui.scroll_down(buf)
end
Expand Down Expand Up @@ -217,87 +241,113 @@ function M.run(adapter, params, config, opts)
end
end

---@diagnostic disable-next-line: missing-parameter
a.run(function()
local results = {}
local lines_buffer = {}
local errored_lines = {}

local run_printer = function()
local print_lines = function()
local new_lines_count = #lines_buffer

if new_lines_count > 0 then
table.insert(lines_buffer, "")
table.insert(lines_buffer, "")

for _, buf in ipairs(ui.get_buffers()) do
local lines_count = vim.api.nvim_buf_line_count(buf)
local should_scroll = ui.should_continue_scroll(buf, lines_count - 2)

if config.use_builtin_colorizer then
printer:set_next_lines(lines_buffer, buf, lines_count - 2)
else
set_ansi_lines(buf, lines_count - 2, -1, false, lines_buffer)
end

for i, _ in ipairs(errored_lines) do
vim.highlight.range(
buf,
stderr_ns,
"DiagnosticError",
{ i + lines_count - 2, 0 },
{ i + lines_count - 2, -1 }
)
end

print_buf_status(buf, lines_count + new_lines_count)
if should_scroll then
ui.scroll_down(buf)
end
end

lines_buffer = {}
errored_lines = {}
end
end

while is_running() do
u.scheduler()

print_lines()
print_status()

vim.cmd("redraw")
u.sleep(100)
end
end)

local last_update_time = 0
local update_interval = 100 -- ms
-- In case new job is started and we don't want to print the previous one
if job.id == current_job.id then
u.scheduler()
print_lines()
print_status()
end
end

---@diagnostic disable-next-line: missing-parameter
a.run(function()
xpcall(run_printer, function(err)
print("Error in async job:", err)
print("Stack trace:", debug.traceback())

notify.error("Test run failed: " .. err)
end)
end)

local results = {}
while is_running() do
local result = receiver.recv()
table.insert(results, result)

u.scheduler()

if not is_running() then
return
end

if result.type == "exit" then
job.exit_code = result.code
job.finished_at = vim.uv.now()
job.status = "finished"

current_job = nil
if adapter.after_run then
u.scheduler()
adapter.after_run(params, results)
end
end

for _, buf in ipairs(ui.get_buffers()) do
local should_scroll = ui.should_continue_scroll(buf)

if result.type == "stdout" then
if result.output then
local lines = vim.split(result.output, "\n")

table.insert(lines, "")
table.insert(lines, "")

if config.use_builtin_colorizer then
printer:set_next_lines(lines, buf, 2)
else
local line_count = vim.api.nvim_buf_line_count(buf)
set_ansi_lines(buf, line_count - 2, -1, false, lines)
end
end
end

if result.type == "stderr" then
if result.output then
local line_count = vim.api.nvim_buf_line_count(buf)
local lines = vim.split(result.output, "\n")
if result.type == "stdout" or result.type == "stderr" then
if result.output then
local lines = vim.split(result.output, "\n")
local new_lines_count = #lines

table.insert(lines, "")
table.insert(lines, "")
if #lines > 0 then
set_ansi_lines(buf, line_count - 2, -1, false, lines)
if new_lines_count > 0 then
local buffer_size = #lines_buffer
for i, line in ipairs(lines) do
table.insert(lines_buffer, line)

for i = 0, #lines - 1 do
vim.api.nvim_buf_add_highlight(buf, -1, "DiagnosticError", line_count - 2 + i, 0, -1)
if result.type == "stderr" then
errored_lines[buffer_size + i] = true
end
end
end
end

local current_time = vim.loop.now()
if (current_time - last_update_time) > update_interval then
u.scheduler(function()
vim.cmd("redraw")
end)
last_update_time = current_time
end

if should_scroll then
ui.scroll_down(buf)
end
end

print_status()
end
end

Expand Down Expand Up @@ -391,15 +441,24 @@ function M.run_previous(config, mode)
end

local bufnr = get_buf_by_name(current_run.bufname)
if bufnr == nil then
local is_loaded = false
if bufnr ~= nil then
is_loaded = vim.api.nvim_buf_is_loaded(bufnr)
end

if not is_loaded then
-- If the buffer doesn't exist, try to open the file
bufnr = vim.fn.bufadd(current_run.bufname)
if bufnr == 0 then
return notify.warn("Failed to open previous run file: " .. current_run.bufname)
end

-- Ensure the buffer is loaded
-- Ensure the buffer is listed
vim.bo[bufnr].buflisted = true

vim.api.nvim_buf_call(bufnr, function()
vim.cmd("silent! e!") -- Try to edit the buffer in place to force load it
end)
end

if vim.bo[bufnr].filetype == "" then
Expand All @@ -425,17 +484,9 @@ function M.kill_current_run()
if current_job then
local job = current_job
vim.system({ "kill", tostring(current_job.pid) }):wait()
current_job = nil

for _, buf in ipairs(ui.get_buffers()) do
local line_count = vim.api.nvim_buf_line_count(buf)

local passedTime = vim.loop.now() - job.started_at
local time_display = string.format("%.2f", passedTime / 1000) .. "s"

vim.api.nvim_buf_set_lines(buf, line_count - 1, line_count, false, { "Cancelled after " .. time_display })
vim.api.nvim_buf_add_highlight(buf, -1, "DiagnosticWarn", line_count - 1, 0, -1)
end
job.status = "canceled"
job.finished_at = vim.uv.now()
end
end

Expand Down
16 changes: 6 additions & 10 deletions lua/quicktest/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ end
M.get_buffers = function()
return { get_split_buf(), get_popup_buf() }
end

M.is_split_opened = function()
return is_buf_visible(get_split_buf())
end
Expand Down Expand Up @@ -160,27 +161,22 @@ function M.scroll_down(buf)
for _, win in ipairs(windows) do
local win_bufnr = vim.api.nvim_win_get_buf(win)
if win_bufnr == buf then
local line_count = vim.api.nvim_buf_line_count(buf)

if line_count < 3 then
return
end

vim.api.nvim_win_set_cursor(win, { line_count - 2, 0 })
vim.api.nvim_win_call(win, function()
vim.cmd("normal! G2k")
end)
end
end
end

---@param buf number
function M.should_continue_scroll(buf)
function M.should_continue_scroll(buf, line_count)
local windows = vim.api.nvim_list_wins()
for _, win in ipairs(windows) do
local win_bufnr = vim.api.nvim_win_get_buf(win)
if win_bufnr == buf then
local current_pos = vim.api.nvim_win_get_cursor(win)
local line_count = vim.api.nvim_buf_line_count(buf)

return current_pos[1] >= line_count - 2
return current_pos[1] >= line_count
end
end
end
Expand Down
Loading