Merge pull request #39 from barrett-ruth/feat/test-mode-horizontal
feat: panel formatting
This commit is contained in:
commit
ff3be54b7f
8 changed files with 113 additions and 109 deletions
|
|
@ -5,13 +5,13 @@ endif
|
||||||
syntax match cpOutputCode /^\[code\]:/
|
syntax match cpOutputCode /^\[code\]:/
|
||||||
syntax match cpOutputTime /^\[time\]:/
|
syntax match cpOutputTime /^\[time\]:/
|
||||||
syntax match cpOutputDebug /^\[debug\]:/
|
syntax match cpOutputDebug /^\[debug\]:/
|
||||||
syntax match cpOutputMatchesTrue /^\[matches\]:\ze true$/
|
syntax match cpOutputOkTrue /^\[ok\]:\ze true$/
|
||||||
syntax match cpOutputMatchesFalse /^\[matches\]:\ze false$/
|
syntax match cpOutputOkFalse /^\[ok\]:\ze false$/
|
||||||
|
|
||||||
highlight default link cpOutputCode DiagnosticInfo
|
highlight default link cpOutputCode DiagnosticInfo
|
||||||
highlight default link cpOutputTime Comment
|
highlight default link cpOutputTime Comment
|
||||||
highlight default link cpOutputDebug Comment
|
highlight default link cpOutputDebug Comment
|
||||||
highlight default link cpOutputMatchesTrue DiffAdd
|
highlight default link cpOutputOkTrue DiffAdd
|
||||||
highlight default link cpOutputMatchesFalse DiffDelete
|
highlight default link cpOutputOkFalse DiffDelete
|
||||||
|
|
||||||
let b:current_syntax = "cp"
|
let b:current_syntax = "cp"
|
||||||
|
|
|
||||||
49
doc/cp.txt
49
doc/cp.txt
|
|
@ -70,23 +70,6 @@ Navigation Commands ~
|
||||||
:CP prev Navigate to previous problem in current contest.
|
:CP prev Navigate to previous problem in current contest.
|
||||||
Stops at first problem (no wrapping).
|
Stops at first problem (no wrapping).
|
||||||
|
|
||||||
ERROR HANDLING *cp-errors*
|
|
||||||
|
|
||||||
cp.nvim provides clear error messages for common issues:
|
|
||||||
|
|
||||||
No Contest Configured ~
|
|
||||||
When running `:CP run`, `:CP debug`, or `:CP test` without setting up a
|
|
||||||
contest first, you'll see:
|
|
||||||
"No contest configured. Use :CP <platform> <contest> <problem> to set up first."
|
|
||||||
|
|
||||||
Platform Not Supported ~
|
|
||||||
For platforms or features not yet implemented:
|
|
||||||
"test panel not yet supported for codeforces"
|
|
||||||
|
|
||||||
Missing Dependencies ~
|
|
||||||
When required tools are missing:
|
|
||||||
"uv is not installed. Install it to enable problem scraping: https://docs.astral.sh/uv/"
|
|
||||||
|
|
||||||
CONFIGURATION *cp-config*
|
CONFIGURATION *cp-config*
|
||||||
|
|
||||||
cp.nvim works out of the box. No setup required.
|
cp.nvim works out of the box. No setup required.
|
||||||
|
|
@ -283,7 +266,11 @@ TEST PANEL *cp-test*
|
||||||
|
|
||||||
The test panel provides individual test case debugging with a three-pane
|
The test panel provides individual test case debugging with a three-pane
|
||||||
layout showing test list, expected output, and actual output side-by-side.
|
layout showing test list, expected output, and actual output side-by-side.
|
||||||
Currently supported for AtCoder and CSES (Codeforces support coming soon).
|
Currently supported for AtCoder and CSES.
|
||||||
|
|
||||||
|
Note: Codeforces is not supported due to the ambiguity of identifying
|
||||||
|
individual test case output. See https://codeforces.com/blog/entry/138406
|
||||||
|
for ongoing efforts to resolve this.
|
||||||
|
|
||||||
Activation ~
|
Activation ~
|
||||||
*:CP-test*
|
*:CP-test*
|
||||||
|
|
@ -314,40 +301,14 @@ The test panel uses a three-pane layout for easy comparison: >
|
||||||
└─────────────────────────┘ └─────────────────────────┘
|
└─────────────────────────┘ └─────────────────────────┘
|
||||||
<
|
<
|
||||||
|
|
||||||
Test Status Indicators ~
|
|
||||||
|
|
||||||
PASS Test case passed (green highlighting)
|
|
||||||
FAIL Test case failed (red highlighting with diff)
|
|
||||||
(empty) Test case not yet executed
|
|
||||||
|
|
||||||
Keymaps ~
|
Keymaps ~
|
||||||
*cp-test-keys*
|
*cp-test-keys*
|
||||||
j / <Down> Navigate to next test case
|
j / <Down> Navigate to next test case
|
||||||
k / <Up> Navigate to previous test case
|
k / <Up> Navigate to previous test case
|
||||||
q Exit test panel (restore layout)
|
q Exit test panel (restore layout)
|
||||||
|
|
||||||
Test Case Sources ~
|
|
||||||
|
|
||||||
Test cases are loaded in priority order:
|
|
||||||
1. Individual scraped test cases from cache (AtCoder, CSES)
|
|
||||||
2. Individual test case files (*.1.cpin, *.2.cpin, etc.)
|
|
||||||
3. Combined input/output files from io/ directory (fallback)
|
|
||||||
|
|
||||||
For AtCoder problems, individual test case files are prefixed with "1\n"
|
|
||||||
to satisfy template requirements, but this prefix is stripped in the UI
|
|
||||||
for clean display.
|
|
||||||
|
|
||||||
For CSES problems, individual test cases are loaded directly from scraped data.
|
|
||||||
|
|
||||||
Execution Details ~
|
Execution Details ~
|
||||||
|
|
||||||
Each test case shows:
|
|
||||||
• Input data provided to your solution (top pane)
|
|
||||||
• Expected output from the problem statement (bottom left pane)
|
|
||||||
• Actual output produced by your solution (bottom right pane)
|
|
||||||
• Execution time in milliseconds
|
|
||||||
• Pass/fail status with vim diff highlighting for failures
|
|
||||||
|
|
||||||
Test cases are executed individually using the same compilation and
|
Test cases are executed individually using the same compilation and
|
||||||
execution pipeline as |:CP-run|, but with isolated input/output for
|
execution pipeline as |:CP-run|, but with isolated input/output for
|
||||||
precise failure analysis. All tests are automatically run when the
|
precise failure analysis. All tests are automatically run when the
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,23 @@ M.canonical_filetypes = {
|
||||||
[M.PYTHON] = "python",
|
[M.PYTHON] = "python",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---@type table<number, string>
|
||||||
|
M.signal_codes = {
|
||||||
|
[128] = "SIGILL",
|
||||||
|
[130] = "SIGINT",
|
||||||
|
[131] = "SIGQUIT",
|
||||||
|
[132] = "SIGILL",
|
||||||
|
[133] = "SIGTRAP",
|
||||||
|
[134] = "SIGABRT",
|
||||||
|
[135] = "SIGBUS",
|
||||||
|
[136] = "SIGFPE",
|
||||||
|
[137] = "SIGKILL",
|
||||||
|
[138] = "SIGUSR1",
|
||||||
|
[139] = "SIGSEGV",
|
||||||
|
[140] = "SIGUSR2",
|
||||||
|
[141] = "SIGPIPE",
|
||||||
|
[142] = "SIGALRM",
|
||||||
|
[143] = "SIGTERM",
|
||||||
|
}
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -64,24 +64,6 @@ local function build_command(cmd_template, executable, substitutions)
|
||||||
return cmd
|
return cmd
|
||||||
end
|
end
|
||||||
|
|
||||||
local signal_codes = {
|
|
||||||
[128] = "SIGILL",
|
|
||||||
[130] = "SIGINT",
|
|
||||||
[131] = "SIGQUIT",
|
|
||||||
[132] = "SIGILL",
|
|
||||||
[133] = "SIGTRAP",
|
|
||||||
[134] = "SIGABRT",
|
|
||||||
[135] = "SIGBUS",
|
|
||||||
[136] = "SIGFPE",
|
|
||||||
[137] = "SIGKILL",
|
|
||||||
[138] = "SIGUSR1",
|
|
||||||
[139] = "SIGSEGV",
|
|
||||||
[140] = "SIGUSR2",
|
|
||||||
[141] = "SIGPIPE",
|
|
||||||
[142] = "SIGALRM",
|
|
||||||
[143] = "SIGTERM",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function ensure_directories()
|
local function ensure_directories()
|
||||||
vim.system({ "mkdir", "-p", "build", "io" }):wait()
|
vim.system({ "mkdir", "-p", "build", "io" }):wait()
|
||||||
end
|
end
|
||||||
|
|
@ -176,7 +158,7 @@ local function format_output(exec_result, expected_file, is_debug)
|
||||||
if exec_result.timed_out then
|
if exec_result.timed_out then
|
||||||
table.insert(metadata_lines, "[code]: 124 (TIMEOUT)")
|
table.insert(metadata_lines, "[code]: 124 (TIMEOUT)")
|
||||||
elseif exec_result.code >= 128 then
|
elseif exec_result.code >= 128 then
|
||||||
local signal_name = signal_codes[exec_result.code] or "SIGNAL"
|
local signal_name = constants.signal_codes[exec_result.code] or "SIGNAL"
|
||||||
table.insert(metadata_lines, ("[code]: %d (%s)"):format(exec_result.code, signal_name))
|
table.insert(metadata_lines, ("[code]: %d (%s)"):format(exec_result.code, signal_name))
|
||||||
else
|
else
|
||||||
table.insert(metadata_lines, ("[code]: %d"):format(exec_result.code))
|
table.insert(metadata_lines, ("[code]: %d"):format(exec_result.code))
|
||||||
|
|
@ -193,17 +175,17 @@ local function format_output(exec_result, expected_file, is_debug)
|
||||||
table.remove(actual_lines)
|
table.remove(actual_lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
local matches = #actual_lines == #expected_content
|
local ok = #actual_lines == #expected_content
|
||||||
if matches then
|
if ok then
|
||||||
for i, line in ipairs(actual_lines) do
|
for i, line in ipairs(actual_lines) do
|
||||||
if line ~= expected_content[i] then
|
if line ~= expected_content[i] then
|
||||||
matches = false
|
ok = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(metadata_lines, ("[matches]: %s"):format(matches and "true" or "false"))
|
table.insert(metadata_lines, ("[ok]: %s"):format(ok and "true" or "false"))
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(output_lines, "") .. "\n" .. table.concat(metadata_lines, "\n")
|
return table.concat(output_lines, "") .. "\n" .. table.concat(metadata_lines, "\n")
|
||||||
|
|
|
||||||
|
|
@ -248,23 +248,23 @@ local function toggle_test_panel()
|
||||||
local expected_buf = vim.api.nvim_create_buf(false, true)
|
local expected_buf = vim.api.nvim_create_buf(false, true)
|
||||||
local actual_buf = vim.api.nvim_create_buf(false, true)
|
local actual_buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
|
||||||
vim.api.nvim_buf_set_option(tab_buf, "bufhidden", "wipe")
|
vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = tab_buf })
|
||||||
vim.api.nvim_buf_set_option(expected_buf, "bufhidden", "wipe")
|
vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = expected_buf })
|
||||||
vim.api.nvim_buf_set_option(actual_buf, "bufhidden", "wipe")
|
vim.api.nvim_set_option_value("bufhidden", "wipe", { buf = actual_buf })
|
||||||
|
|
||||||
local main_win = vim.api.nvim_get_current_win()
|
local main_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
||||||
vim.api.nvim_buf_set_option(tab_buf, "filetype", "cptest")
|
vim.api.nvim_set_option_value("filetype", "cptest", { buf = tab_buf })
|
||||||
|
|
||||||
vim.cmd("split")
|
vim.cmd("split")
|
||||||
local content_win = vim.api.nvim_get_current_win()
|
local content_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(content_win, actual_buf)
|
vim.api.nvim_win_set_buf(content_win, actual_buf)
|
||||||
vim.api.nvim_buf_set_option(actual_buf, "filetype", "cptest")
|
vim.api.nvim_set_option_value("filetype", "cptest", { buf = actual_buf })
|
||||||
|
|
||||||
vim.cmd("vsplit")
|
vim.cmd("vsplit")
|
||||||
local expected_win = vim.api.nvim_get_current_win()
|
local expected_win = vim.api.nvim_get_current_win()
|
||||||
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
||||||
vim.api.nvim_buf_set_option(expected_buf, "filetype", "cptest")
|
vim.api.nvim_set_option_value("filetype", "cptest", { buf = expected_buf })
|
||||||
|
|
||||||
local test_windows = {
|
local test_windows = {
|
||||||
tab_win = main_win,
|
tab_win = main_win,
|
||||||
|
|
@ -281,10 +281,45 @@ local function toggle_test_panel()
|
||||||
local test_state = test_module.get_test_panel_state()
|
local test_state = test_module.get_test_panel_state()
|
||||||
local tab_lines = {}
|
local tab_lines = {}
|
||||||
|
|
||||||
|
local max_status_width = 0
|
||||||
|
local max_code_width = 0
|
||||||
|
local max_time_width = 0
|
||||||
|
|
||||||
|
for _, test_case in ipairs(test_state.test_cases) do
|
||||||
|
local status_text = test_case.status == "pending" and "" or string.upper(test_case.status)
|
||||||
|
max_status_width = math.max(max_status_width, #status_text)
|
||||||
|
|
||||||
|
if test_case.code then
|
||||||
|
max_code_width = math.max(max_code_width, #tostring(test_case.code))
|
||||||
|
end
|
||||||
|
|
||||||
|
if test_case.time_ms then
|
||||||
|
local time_text = string.format("%.0fms", test_case.time_ms)
|
||||||
|
max_time_width = math.max(max_time_width, #time_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for i, test_case in ipairs(test_state.test_cases) do
|
for i, test_case in ipairs(test_state.test_cases) do
|
||||||
local status_text = test_case.status == "pending" and "?" or string.upper(test_case.status)
|
|
||||||
local prefix = i == test_state.current_index and "> " or " "
|
local prefix = i == test_state.current_index and "> " or " "
|
||||||
local tab = string.format("%s%d. %s", prefix, i, status_text)
|
local tab = string.format("%s%d.", prefix, i)
|
||||||
|
|
||||||
|
if test_case.ok ~= nil then
|
||||||
|
tab = tab .. string.format(" [ok:%-5s]", tostring(test_case.ok))
|
||||||
|
end
|
||||||
|
|
||||||
|
if test_case.code then
|
||||||
|
tab = tab .. string.format(" [code:%-" .. max_code_width .. "s]", tostring(test_case.code))
|
||||||
|
end
|
||||||
|
|
||||||
|
if test_case.time_ms then
|
||||||
|
local time_text = string.format("%.0fms", test_case.time_ms)
|
||||||
|
tab = tab .. string.format(" [time:%-" .. max_time_width .. "s]", time_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
if test_case.signal then
|
||||||
|
tab = tab .. string.format(" [%s]", test_case.signal)
|
||||||
|
end
|
||||||
|
|
||||||
table.insert(tab_lines, tab)
|
table.insert(tab_lines, tab)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -292,7 +327,6 @@ local function toggle_test_panel()
|
||||||
if current_test then
|
if current_test then
|
||||||
table.insert(tab_lines, "")
|
table.insert(tab_lines, "")
|
||||||
table.insert(tab_lines, "Input:")
|
table.insert(tab_lines, "Input:")
|
||||||
table.insert(tab_lines, "")
|
|
||||||
for _, line in ipairs(vim.split(current_test.input, "\n", { plain = true, trimempty = true })) do
|
for _, line in ipairs(vim.split(current_test.input, "\n", { plain = true, trimempty = true })) do
|
||||||
table.insert(tab_lines, line)
|
table.insert(tab_lines, line)
|
||||||
end
|
end
|
||||||
|
|
@ -310,8 +344,6 @@ local function toggle_test_panel()
|
||||||
end
|
end
|
||||||
|
|
||||||
local expected_lines = {}
|
local expected_lines = {}
|
||||||
table.insert(expected_lines, "Expected:")
|
|
||||||
table.insert(expected_lines, "")
|
|
||||||
|
|
||||||
local expected_text = current_test.expected
|
local expected_text = current_test.expected
|
||||||
for _, line in ipairs(vim.split(expected_text, "\n", { plain = true, trimempty = true })) do
|
for _, line in ipairs(vim.split(expected_text, "\n", { plain = true, trimempty = true })) do
|
||||||
|
|
@ -332,25 +364,22 @@ local function toggle_test_panel()
|
||||||
local actual_lines = {}
|
local actual_lines = {}
|
||||||
|
|
||||||
if current_test.actual then
|
if current_test.actual then
|
||||||
table.insert(actual_lines, "Actual:")
|
|
||||||
table.insert(actual_lines, "")
|
|
||||||
for _, line in ipairs(vim.split(current_test.actual, "\n", { plain = true, trimempty = true })) do
|
for _, line in ipairs(vim.split(current_test.actual, "\n", { plain = true, trimempty = true })) do
|
||||||
table.insert(actual_lines, line)
|
table.insert(actual_lines, line)
|
||||||
end
|
end
|
||||||
|
|
||||||
if current_test.status == "fail" then
|
if current_test.status == "fail" then
|
||||||
vim.api.nvim_win_set_option(test_windows.expected_win, "diff", true)
|
vim.api.nvim_set_option_value("diff", true, { win = test_windows.expected_win })
|
||||||
vim.api.nvim_win_set_option(test_windows.actual_win, "diff", true)
|
vim.api.nvim_set_option_value("diff", true, { win = test_windows.actual_win })
|
||||||
else
|
else
|
||||||
vim.api.nvim_win_set_option(test_windows.expected_win, "diff", false)
|
vim.api.nvim_set_option_value("diff", false, { win = test_windows.expected_win })
|
||||||
vim.api.nvim_win_set_option(test_windows.actual_win, "diff", false)
|
vim.api.nvim_set_option_value("diff", false, { win = test_windows.actual_win })
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(actual_lines, "Actual:")
|
|
||||||
table.insert(actual_lines, "(not run yet)")
|
table.insert(actual_lines, "(not run yet)")
|
||||||
|
|
||||||
vim.api.nvim_win_set_option(test_windows.expected_win, "diff", false)
|
vim.api.nvim_set_option_value("diff", false, { win = test_windows.expected_win })
|
||||||
vim.api.nvim_win_set_option(test_windows.actual_win, "diff", false)
|
vim.api.nvim_set_option_value("diff", false, { win = test_windows.actual_win })
|
||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_buf_set_lines(test_buffers.actual_buf, 0, -1, false, actual_lines)
|
vim.api.nvim_buf_set_lines(test_buffers.actual_buf, 0, -1, false, actual_lines)
|
||||||
|
|
|
||||||
|
|
@ -259,10 +259,6 @@ function M.scrape_problem(ctx)
|
||||||
local input_content = test_case.input:gsub("\r", "")
|
local input_content = test_case.input:gsub("\r", "")
|
||||||
local expected_content = test_case.output:gsub("\r", "")
|
local expected_content = test_case.output:gsub("\r", "")
|
||||||
|
|
||||||
if ctx.contest == "atcoder" then
|
|
||||||
input_content = "1\n" .. input_content
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.fn.writefile(vim.split(input_content, "\n", true), input_file)
|
vim.fn.writefile(vim.split(input_content, "\n", true), input_file)
|
||||||
vim.fn.writefile(vim.split(expected_content, "\n", true), expected_file)
|
vim.fn.writefile(vim.split(expected_content, "\n", true), expected_file)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
---@field time_ms number?
|
---@field time_ms number?
|
||||||
---@field error string?
|
---@field error string?
|
||||||
---@field selected boolean
|
---@field selected boolean
|
||||||
|
---@field code number?
|
||||||
|
---@field ok boolean?
|
||||||
|
---@field signal string?
|
||||||
|
---@field timed_out boolean?
|
||||||
|
|
||||||
---@class TestPanelState
|
---@class TestPanelState
|
||||||
---@field test_cases TestCase[]
|
---@field test_cases TestCase[]
|
||||||
|
|
@ -18,6 +22,7 @@
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
local logger = require("cp.log")
|
local logger = require("cp.log")
|
||||||
|
local constants = require("cp.constants")
|
||||||
|
|
||||||
---@type TestPanelState
|
---@type TestPanelState
|
||||||
local test_panel_state = {
|
local test_panel_state = {
|
||||||
|
|
@ -89,10 +94,6 @@ local function parse_test_cases_from_files(input_file, expected_file)
|
||||||
local input_content = table.concat(vim.fn.readfile(individual_input_file), "\n")
|
local input_content = table.concat(vim.fn.readfile(individual_input_file), "\n")
|
||||||
local expected_content = table.concat(vim.fn.readfile(individual_expected_file), "\n")
|
local expected_content = table.concat(vim.fn.readfile(individual_expected_file), "\n")
|
||||||
|
|
||||||
if input_content:match("^1\n") then
|
|
||||||
input_content = input_content:gsub("^1\n", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(test_cases, create_test_case(i, input_content, expected_content))
|
table.insert(test_cases, create_test_case(i, input_content, expected_content))
|
||||||
i = i + 1
|
i = i + 1
|
||||||
else
|
else
|
||||||
|
|
@ -115,7 +116,6 @@ end
|
||||||
---@return table
|
---@return table
|
||||||
local function run_single_test_case(ctx, contest_config, test_case)
|
local function run_single_test_case(ctx, contest_config, test_case)
|
||||||
local language = vim.fn.fnamemodify(ctx.source_file, ":e")
|
local language = vim.fn.fnamemodify(ctx.source_file, ":e")
|
||||||
local constants = require("cp.constants")
|
|
||||||
local language_name = constants.filetype_to_language[language] or contest_config.default_language
|
local language_name = constants.filetype_to_language[language] or contest_config.default_language
|
||||||
local language_config = contest_config[language_name]
|
local language_config = contest_config[language_name]
|
||||||
|
|
||||||
|
|
@ -170,9 +170,14 @@ local function run_single_test_case(ctx, contest_config, test_case)
|
||||||
|
|
||||||
local run_cmd = build_command(language_config.run, language_config.executable, substitutions)
|
local run_cmd = build_command(language_config.run, language_config.executable, substitutions)
|
||||||
|
|
||||||
|
local stdin_content = test_case.input .. "\n"
|
||||||
|
if ctx.contest == "atcoder" then
|
||||||
|
stdin_content = "1\n" .. stdin_content
|
||||||
|
end
|
||||||
|
|
||||||
local start_time = vim.uv.hrtime()
|
local start_time = vim.uv.hrtime()
|
||||||
local result = vim.system(run_cmd, {
|
local result = vim.system(run_cmd, {
|
||||||
stdin = test_case.input .. "\n",
|
stdin = stdin_content,
|
||||||
timeout = contest_config.timeout_ms or 2000,
|
timeout = contest_config.timeout_ms or 2000,
|
||||||
text = true,
|
text = true,
|
||||||
}):wait()
|
}):wait()
|
||||||
|
|
@ -180,22 +185,32 @@ local function run_single_test_case(ctx, contest_config, test_case)
|
||||||
|
|
||||||
local actual_output = (result.stdout or ""):gsub("\n$", "")
|
local actual_output = (result.stdout or ""):gsub("\n$", "")
|
||||||
local expected_output = test_case.expected:gsub("\n$", "")
|
local expected_output = test_case.expected:gsub("\n$", "")
|
||||||
local matches = actual_output == expected_output
|
local ok = actual_output == expected_output
|
||||||
|
|
||||||
local status
|
local status
|
||||||
if result.code == 143 or result.code == 124 then
|
local timed_out = result.code == 143 or result.code == 124
|
||||||
|
if timed_out then
|
||||||
status = "timeout"
|
status = "timeout"
|
||||||
elseif result.code == 0 and matches then
|
elseif result.code == 0 and ok then
|
||||||
status = "pass"
|
status = "pass"
|
||||||
else
|
else
|
||||||
status = "fail"
|
status = "fail"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local signal = nil
|
||||||
|
if result.code >= 128 then
|
||||||
|
signal = constants.signal_codes[result.code]
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status = status,
|
status = status,
|
||||||
actual = actual_output,
|
actual = actual_output,
|
||||||
error = result.code ~= 0 and result.stderr or nil,
|
error = result.code ~= 0 and result.stderr or nil,
|
||||||
time_ms = execution_time,
|
time_ms = execution_time,
|
||||||
|
code = result.code,
|
||||||
|
ok = ok,
|
||||||
|
signal = signal,
|
||||||
|
timed_out = timed_out,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -235,6 +250,10 @@ function M.run_test_case(ctx, contest_config, index)
|
||||||
test_case.actual = result.actual
|
test_case.actual = result.actual
|
||||||
test_case.error = result.error
|
test_case.error = result.error
|
||||||
test_case.time_ms = result.time_ms
|
test_case.time_ms = result.time_ms
|
||||||
|
test_case.code = result.code
|
||||||
|
test_case.ok = result.ok
|
||||||
|
test_case.signal = result.signal
|
||||||
|
test_case.timed_out = result.timed_out
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
neovim plugin for competitive programming.
|
neovim plugin for competitive programming.
|
||||||
|
|
||||||
https://private-user-images.githubusercontent.com/62671086/489116291-391976d1-c2f4-49e6-a79d-13ff05e9be86.mp4?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NTc3NDQ1ODEsIm5iZiI6MTc1Nzc0NDI4MSwicGF0aCI6Ii82MjY3MTA4Ni80ODkxMTYyOTEtMzkxOTc2ZDEtYzJmNC00OWU2LWE3OWQtMTNmZjA1ZTliZTg2Lm1wND9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA5MTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwOTEzVDA2MTgwMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWI0Zjc0YmQzNWIzNGZkM2VjZjM3NGM0YmZmM2I3MmJkZGQ0YTczYjIxMTFiODc3MjQyMzY3ODc2ZTUxZDRkMzkmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.MBK5q_Zxr0gWuzfjwmSbB7P7dtWrATrT5cDOosdPRuQ
|
https://github.com/user-attachments/assets/cb142535-fba0-4280-8f11-66ad1ca50ca9
|
||||||
|
|
||||||
[video config](https://github.com/barrett-ruth/dots/blob/main/nvim/lua/plugins/cp.lua)
|
[video config](https://github.com/barrett-ruth/dots/blob/main/nvim/lua/plugins/cp.lua)
|
||||||
|
|
||||||
|
|
@ -68,6 +68,4 @@ follows:
|
||||||
|
|
||||||
- finer-tuned problem limits (i.e. per-problem codeforces time, memory)
|
- finer-tuned problem limits (i.e. per-problem codeforces time, memory)
|
||||||
- better highlighting
|
- better highlighting
|
||||||
- test case management
|
- notify discord members
|
||||||
- new video with functionality, notify discord members
|
|
||||||
- note that codeforces support is scuffed: https://codeforces.com/blog/entry/146423
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue