diff --git a/lua/cp/runner/run.lua b/lua/cp/runner/run.lua index 436639b..411769d 100644 --- a/lua/cp/runner/run.lua +++ b/lua/cp/runner/run.lua @@ -14,6 +14,7 @@ ---@field signal string? ---@field tled boolean? ---@field mled boolean? +---@field rss_mb number ---@class ProblemConstraints ---@field timeout_ms number @@ -44,6 +45,10 @@ local run_panel_state = { constraints = nil, } +---@param platform string +---@param contest_id string +---@param problem_id string|nil +---@return ProblemConstraints|nil local function load_constraints_from_cache(platform, contest_id, problem_id) cache.load() local timeout_ms, memory_mb = cache.get_constraints(platform, contest_id, problem_id) @@ -53,6 +58,8 @@ local function load_constraints_from_cache(platform, contest_id, problem_id) return nil end +---@param test_cases TestCase[] +---@return RanTestCase[] local function create_sentinal_panel_data(test_cases) local out = {} for i, tc in ipairs(test_cases) do @@ -67,11 +74,18 @@ local function create_sentinal_panel_data(test_cases) return out end +---@param language_config LanguageConfig +---@param substitutions table +---@return string[] local function build_command(language_config, substitutions) local exec_util = require('cp.runner.execute')._util return exec_util.build_command(language_config.test, language_config.executable, substitutions) end +---@param contest_config ContestConfig +---@param cp_config cp.Config +---@param test_case RanTestCase +---@return { status: "pass"|"fail"|"tle"|"mle", actual: string, actual_highlights: Highlight[], error: string, stderr: string, time_ms: number, code: integer, ok: boolean, signal: string, tled: boolean, mled: boolean, rss_mb: number } local function run_single_test_case(contest_config, cp_config, test_case) local state = require('cp.state') local exec = require('cp.runner.execute') @@ -96,6 +110,7 @@ local function run_single_test_case(contest_config, cp_config, test_case) error = 'Compilation failed', stderr = clean, time_ms = 0, + rss_mb = 0, code = cr.code, ok = false, signal = nil, @@ -161,7 +176,7 @@ local function run_single_test_case(contest_config, cp_config, test_case) status = status, actual = out, actual_highlights = highlights, - error = (r.code ~= 0 and not ok) and out or nil, + error = (r.code ~= 0 and not ok) and out or '', stderr = '', time_ms = r.time_ms, code = r.code, @@ -169,9 +184,12 @@ local function run_single_test_case(contest_config, cp_config, test_case) signal = signal, tled = r.tled or false, mled = r.mled or false, + rss_mb = r.peak_mb, } end +---@param state table +---@return boolean function M.load_test_cases(state) local tcs = cache.get_test_cases( state.get_platform() or '', @@ -191,6 +209,10 @@ function M.load_test_cases(state) return #tcs > 0 end +---@param contest_config ContestConfig +---@param cp_config cp.Config +---@param index number +---@return boolean function M.run_test_case(contest_config, cp_config, index) local tc = run_panel_state.test_cases[index] if not tc then @@ -211,10 +233,14 @@ function M.run_test_case(contest_config, cp_config, index) tc.signal = r.signal tc.tled = r.tled tc.mled = r.mled + tc.rss_mb = r.rss_mb return true end +---@param contest_config ContestConfig +---@param cp_config cp.Config +---@return RanTestCase[] function M.run_all_test_cases(contest_config, cp_config) local results = {} for i = 1, #run_panel_state.test_cases do @@ -224,11 +250,14 @@ function M.run_all_test_cases(contest_config, cp_config) return results end +---@return RunPanelState function M.get_run_panel_state() return run_panel_state end -function M.handle_compilation_failure(compilation_output) +---@param output string|nil +---@return nil +function M.handle_compilation_failure(output) local ansi = require('cp.ui.ansi') local config = require('cp.config').setup() @@ -236,11 +265,11 @@ function M.handle_compilation_failure(compilation_output) local hl = {} if config.run_panel.ansi then - local p = ansi.parse_ansi_text(compilation_output or '') + local p = ansi.parse_ansi_text(output or '') txt = table.concat(p.lines, '\n') hl = p.highlights else - txt = (compilation_output or ''):gsub('\027%[[%d;]*[a-zA-Z]', '') + txt = (output or ''):gsub('\027%[[%d;]*[a-zA-Z]', '') end for _, tc in ipairs(run_panel_state.test_cases) do @@ -252,7 +281,7 @@ function M.handle_compilation_failure(compilation_output) tc.time_ms = 0 tc.code = 1 tc.ok = false - tc.signal = nil + tc.signal = '' tc.tled = false tc.mled = false end diff --git a/lua/cp/runner/run_render.lua b/lua/cp/runner/run_render.lua index 324c359..f6a14da 100644 --- a/lua/cp/runner/run_render.lua +++ b/lua/cp/runner/run_render.lua @@ -54,18 +54,16 @@ local function format_exit_code(code) end local function compute_cols(test_state) - local w = { num = 5, status = 8, time = 6, timeout = 8, memory = 8, exit = 11 } + local w = { num = 5, status = 8, time = 6, timeout = 8, rss = 8, memory = 8, exit = 11 } - local timeout_str = '' - local memory_str = '' + local timeout_str = '—' + local memory_str = '—' if test_state.constraints then timeout_str = tostring(test_state.constraints.timeout_ms) memory_str = string.format('%.0f', test_state.constraints.memory_mb) - else - timeout_str = '—' - memory_str = '—' end + vim.print(test_state) for i, tc in ipairs(test_state.test_cases) do local prefix = (i == test_state.current_index) and '>' or ' ' w.num = math.max(w.num, #(' ' .. prefix .. i .. ' ')) @@ -73,6 +71,8 @@ local function compute_cols(test_state) local time_str = tc.time_ms and string.format('%.2f', tc.time_ms) or '—' w.time = math.max(w.time, #(' ' .. time_str .. ' ')) w.timeout = math.max(w.timeout, #(' ' .. timeout_str .. ' ')) + local rss_str = (tc.rss_mb and string.format('%.0f', tc.rss_mb)) or '—' + w.rss = math.max(w.rss, #(' ' .. rss_str .. ' ')) w.memory = math.max(w.memory, #(' ' .. memory_str .. ' ')) w.exit = math.max(w.exit, #(' ' .. format_exit_code(tc.code) .. ' ')) end @@ -81,11 +81,12 @@ local function compute_cols(test_state) w.status = math.max(w.status, #' Status ') w.time = math.max(w.time, #' Runtime (ms) ') w.timeout = math.max(w.timeout, #' Time (ms) ') + w.rss = math.max(w.rss, #' RSS (MB) ') w.memory = math.max(w.memory, #' Mem (MB) ') w.exit = math.max(w.exit, #' Exit Code ') - local sum = w.num + w.status + w.time + w.timeout + w.memory + w.exit - local inner = sum + 5 + local sum = w.num + w.status + w.time + w.timeout + w.rss + w.memory + w.exit + local inner = sum + 6 local total = inner + 2 return { w = w, sum = sum, inner = inner, total = total } end @@ -99,15 +100,6 @@ local function center(text, width) return string.rep(' ', left) .. text .. string.rep(' ', pad - left) end -local function right_align(text, width) - local content = (' %s '):format(text) - local pad = width - #content - if pad <= 0 then - return content - end - return string.rep(' ', pad) .. content -end - local function format_num_column(prefix, idx, width) local num_str = tostring(idx) local content @@ -136,6 +128,8 @@ local function top_border(c) .. '┬' .. string.rep('─', w.timeout) .. '┬' + .. string.rep('─', w.rss) + .. '┬' .. string.rep('─', w.memory) .. '┬' .. string.rep('─', w.exit) @@ -153,6 +147,8 @@ local function row_sep(c) .. '┼' .. string.rep('─', w.timeout) .. '┼' + .. string.rep('─', w.rss) + .. '┼' .. string.rep('─', w.memory) .. '┼' .. string.rep('─', w.exit) @@ -170,6 +166,8 @@ local function bottom_border(c) .. '┴' .. string.rep('─', w.timeout) .. '┴' + .. string.rep('─', w.rss) + .. '┴' .. string.rep('─', w.memory) .. '┴' .. string.rep('─', w.exit) @@ -187,6 +185,8 @@ local function flat_fence_above(c) .. '┴' .. string.rep('─', w.timeout) .. '┴' + .. string.rep('─', w.rss) + .. '┴' .. string.rep('─', w.memory) .. '┴' .. string.rep('─', w.exit) @@ -204,6 +204,8 @@ local function flat_fence_below(c) .. '┬' .. string.rep('─', w.timeout) .. '┬' + .. string.rep('─', w.rss) + .. '┬' .. string.rep('─', w.memory) .. '┬' .. string.rep('─', w.exit) @@ -225,6 +227,8 @@ local function header_line(c) .. '│' .. center('Time (ms)', w.timeout) .. '│' + .. center('RSS (MB)', w.rss) + .. '│' .. center('Mem (MB)', w.memory) .. '│' .. center('Exit Code', w.exit) @@ -238,33 +242,34 @@ local function data_row(c, idx, tc, is_current, test_state) local time = tc.time_ms and string.format('%.2f', tc.time_ms) or '—' local exit = format_exit_code(tc.code) - local timeout = '' - local memory = '' + local timeout = '—' + local memory = '—' if test_state.constraints then timeout = tostring(test_state.constraints.timeout_ms) memory = string.format('%.0f', test_state.constraints.memory_mb) - else - timeout = '—' - memory = '—' end + local rss = (tc.rss_mb and string.format('%.0f', tc.rss_mb)) or '—' + local line = '│' .. format_num_column(prefix, idx, w.num) .. '│' - .. right_align(status.text, w.status) + .. center(status.text, w.status) .. '│' - .. right_align(time, w.time) + .. center(time, w.time) .. '│' - .. right_align(timeout, w.timeout) + .. center(timeout, w.timeout) .. '│' - .. right_align(memory, w.memory) + .. center(rss, w.rss) .. '│' - .. right_align(exit, w.exit) + .. center(memory, w.memory) + .. '│' + .. center(exit, w.exit) .. '│' local hi if status.text ~= '' then - local status_pos = line:find(status.text) + local status_pos = line:find(status.text, 1, true) if status_pos then hi = { col_start = status_pos - 1,