commit
a45657c583
11 changed files with 436 additions and 37 deletions
|
|
@ -102,6 +102,27 @@ COMMANDS *cp-commands*
|
|||
:CP C --lang python
|
||||
<
|
||||
|
||||
Edit Commands ~
|
||||
:CP edit [n]
|
||||
Open grid test editor showing all test cases.
|
||||
Tests displayed as 2×N grid (2 rows, N columns):
|
||||
• Top row: Test inputs (editable)
|
||||
• Bottom row: Expected outputs (editable)
|
||||
|
||||
Optional [n]: Jump cursor to test n's input buffer
|
||||
|
||||
Changes saved to both cache and disk on exit,
|
||||
taking effect immediately in :CP run and CLI.
|
||||
|
||||
Keybindings:
|
||||
q Save all and exit editor
|
||||
<c-w> Normal window navigation
|
||||
|
||||
Examples: >
|
||||
:CP edit " Edit all tests
|
||||
:CP edit 3 " Edit all, start at test 3
|
||||
<
|
||||
|
||||
State Restoration ~
|
||||
:CP Restore state from current file.
|
||||
Automatically detects platform, contest, problem,
|
||||
|
|
@ -109,10 +130,16 @@ COMMANDS *cp-commands*
|
|||
switching files to restore your CP environment.
|
||||
|
||||
Cache Commands ~
|
||||
:CP cache clear [contest]
|
||||
Clear the cache data for the specified contest,
|
||||
or all contests if none specified.
|
||||
|
||||
:CP cache clear [platform] [contest]
|
||||
Clear cache data at different granularities:
|
||||
• No args: Clear all cached data
|
||||
• [platform]: Clear all data for a platform
|
||||
• [platform] [contest]: Clear specific contest
|
||||
Examples: >
|
||||
:CP cache clear
|
||||
:CP cache clear codeforces
|
||||
:CP cache clear codeforces 1848
|
||||
<
|
||||
:CP cache read
|
||||
View the cache in a pretty-printed lua buffer.
|
||||
Exit with q.
|
||||
|
|
@ -563,14 +590,18 @@ Input: |VerdictFormatData| table with test results
|
|||
Output: |VerdictFormatResult| table with formatted line and optional highlights
|
||||
|
||||
*VerdictFormatData*
|
||||
{index} (integer) Test case number
|
||||
{status} (table) { text: string, highlight_group: string }
|
||||
{time_ms} (number) Execution time in milliseconds
|
||||
{time_limit_ms} (number) Time limit in milliseconds
|
||||
{memory_mb} (number) Peak memory usage in megabytes
|
||||
{memory_limit_mb} (number) Memory limit in megabytes
|
||||
{exit_code} (integer) Process exit code
|
||||
{signal} (string|nil) Signal name for crashes (e.g. "SIGSEGV")
|
||||
{index} (integer) Test case number
|
||||
{status} (table) { text: string, highlight_group: string }
|
||||
{time_ms} (number) Execution time in milliseconds
|
||||
{time_limit_ms} (number) Time limit in milliseconds
|
||||
{memory_mb} (number) Peak memory usage in megabytes
|
||||
{memory_limit_mb} (number) Memory limit in megabytes
|
||||
{exit_code} (integer) Process exit code
|
||||
{signal} (string|nil) Signal name for crashes (e.g. "SIGSEGV")
|
||||
{time_actual_width} (integer|nil) Dynamic width for time value alignment
|
||||
{time_limit_width} (integer|nil) Dynamic width for time limit alignment
|
||||
{mem_actual_width} (integer|nil) Dynamic width for memory value alignment
|
||||
{mem_limit_width} (integer|nil) Dynamic width for memory limit alignment
|
||||
|
||||
*VerdictFormatResult*
|
||||
{line} (string) The formatted verdict line
|
||||
|
|
|
|||
|
|
@ -39,7 +39,21 @@ function M.handle_cache_command(cmd)
|
|||
vim.api.nvim_set_current_buf(buf)
|
||||
elseif cmd.subcommand == 'clear' then
|
||||
cache.load()
|
||||
if cmd.platform then
|
||||
if cmd.platform and cmd.contest then
|
||||
if vim.tbl_contains(platforms, cmd.platform) then
|
||||
cache.clear_contest_data(cmd.platform, cmd.contest)
|
||||
logger.log(
|
||||
("Cache cleared for %s contest '%s'"):format(
|
||||
constants.PLATFORM_DISPLAY_NAMES[cmd.platform],
|
||||
cmd.contest
|
||||
),
|
||||
vim.log.levels.INFO,
|
||||
true
|
||||
)
|
||||
else
|
||||
logger.log(("Unknown platform '%s'."):format(cmd.platform), vim.log.levels.ERROR)
|
||||
end
|
||||
elseif cmd.platform then
|
||||
if vim.tbl_contains(platforms, cmd.platform) then
|
||||
cache.clear_platform(cmd.platform)
|
||||
logger.log(
|
||||
|
|
|
|||
|
|
@ -40,10 +40,12 @@ local function parse_command(args)
|
|||
end
|
||||
if vim.tbl_contains({ 'clear', 'read' }, subcommand) then
|
||||
local platform = args[3]
|
||||
local contest = args[4]
|
||||
return {
|
||||
type = 'cache',
|
||||
subcommand = subcommand,
|
||||
platform = platform,
|
||||
contest = contest,
|
||||
}
|
||||
else
|
||||
return { type = 'error', message = 'unknown cache subcommand: ' .. subcommand }
|
||||
|
|
@ -55,6 +57,22 @@ local function parse_command(args)
|
|||
else
|
||||
return { type = 'action', action = 'interact' }
|
||||
end
|
||||
elseif first == 'edit' then
|
||||
local test_index = nil
|
||||
if #args >= 2 then
|
||||
local idx = tonumber(args[2])
|
||||
if not idx then
|
||||
return {
|
||||
type = 'error',
|
||||
message = ("Invalid argument '%s': expected test number"):format(args[2]),
|
||||
}
|
||||
end
|
||||
if idx < 1 or idx ~= math.floor(idx) then
|
||||
return { type = 'error', message = ("'%s' is not a valid test index"):format(idx) }
|
||||
end
|
||||
test_index = idx
|
||||
end
|
||||
return { type = 'action', action = 'edit', test_index = test_index }
|
||||
elseif first == 'run' or first == 'panel' then
|
||||
local debug = false
|
||||
local test_index = nil
|
||||
|
|
@ -189,6 +207,9 @@ function M.handle_command(opts)
|
|||
elseif cmd.action == 'pick' then
|
||||
local picker = require('cp.commands.picker')
|
||||
picker.handle_pick_action(cmd.language)
|
||||
elseif cmd.action == 'edit' then
|
||||
local edit = require('cp.ui.edit')
|
||||
edit.toggle_edit(cmd.test_index)
|
||||
end
|
||||
elseif cmd.type == 'problem_jump' then
|
||||
local platform = state.get_platform()
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@
|
|||
---@field memory_limit_mb number
|
||||
---@field exit_code integer
|
||||
---@field signal string|nil
|
||||
---@field time_actual_width? integer
|
||||
---@field time_limit_width? integer
|
||||
---@field mem_actual_width? integer
|
||||
---@field mem_limit_width? integer
|
||||
|
||||
---@class VerdictHighlight
|
||||
---@field col_start integer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
local M = {}
|
||||
|
||||
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
|
||||
M.ACTIONS = { 'run', 'panel', 'next', 'prev', 'pick', 'cache', 'interact' }
|
||||
M.ACTIONS = { 'run', 'panel', 'next', 'prev', 'pick', 'cache', 'interact', 'edit' }
|
||||
|
||||
M.PLATFORM_DISPLAY_NAMES = {
|
||||
atcoder = 'AtCoder',
|
||||
|
|
|
|||
|
|
@ -51,17 +51,28 @@ end
|
|||
---@param data VerdictFormatData
|
||||
---@return VerdictFormatResult
|
||||
function M.default_verdict_formatter(data)
|
||||
local time_data = string.format('%.2f', data.time_ms) .. '/' .. data.time_limit_ms
|
||||
local mem_data = string.format('%.0f', data.memory_mb)
|
||||
.. '/'
|
||||
.. string.format('%.0f', data.memory_limit_mb)
|
||||
local time_actual = string.format('%.2f', data.time_ms)
|
||||
local time_limit = tostring(data.time_limit_ms)
|
||||
local mem_actual = string.format('%.0f', data.memory_mb)
|
||||
local mem_limit = string.format('%.0f', data.memory_limit_mb)
|
||||
local exit_str = data.signal and string.format('%d (%s)', data.exit_code, data.signal)
|
||||
or tostring(data.exit_code)
|
||||
|
||||
local time_actual_w = data.time_actual_width or 6
|
||||
local time_limit_w = data.time_limit_width or 4
|
||||
local mem_actual_w = data.mem_actual_width or 3
|
||||
local mem_limit_w = data.mem_limit_width or 3
|
||||
|
||||
local test_num_part = 'Test ' .. data.index .. ':'
|
||||
local status_part = M.pad_right(data.status.text, 3)
|
||||
local time_part = time_data .. ' ms'
|
||||
local mem_part = mem_data .. ' MB'
|
||||
local time_part = M.pad_left(time_actual, time_actual_w)
|
||||
.. '/'
|
||||
.. M.pad_left(time_limit, time_limit_w)
|
||||
.. ' ms'
|
||||
local mem_part = M.pad_left(mem_actual, mem_actual_w)
|
||||
.. '/'
|
||||
.. M.pad_left(mem_limit, mem_limit_w)
|
||||
.. ' MB'
|
||||
local exit_part = 'exit: ' .. exit_str
|
||||
|
||||
local line = test_num_part
|
||||
|
|
|
|||
|
|
@ -244,7 +244,12 @@ function M.setup_problem(problem_id, language)
|
|||
if vim.api.nvim_buf_is_valid(prov.bufnr) then
|
||||
vim.api.nvim_buf_set_name(prov.bufnr, source_file)
|
||||
vim.bo[prov.bufnr].swapfile = true
|
||||
vim.cmd(string.format('silent keepalt noautocmd write! %s', vim.fn.fnameescape(source_file)))
|
||||
-- selene: allow(mixed_table)
|
||||
vim.cmd.write({
|
||||
vim.fn.fnameescape(source_file),
|
||||
bang = true,
|
||||
mods = { silent = true, noautocmd = true, keepalt = true },
|
||||
})
|
||||
state.set_solution_win(vim.api.nvim_get_current_win())
|
||||
if config.hooks and config.hooks.setup_code and not vim.b[prov.bufnr].cp_setup_done then
|
||||
local ok = pcall(config.hooks.setup_code, state)
|
||||
|
|
|
|||
246
lua/cp/ui/edit.lua
Normal file
246
lua/cp/ui/edit.lua
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
local M = {}
|
||||
|
||||
local cache = require('cp.cache')
|
||||
local config_module = require('cp.config')
|
||||
local helpers = require('cp.helpers')
|
||||
local logger = require('cp.log')
|
||||
local state = require('cp.state')
|
||||
local utils = require('cp.utils')
|
||||
|
||||
---@class TestBufferPair
|
||||
---@field input_buf integer
|
||||
---@field expected_buf integer
|
||||
---@field input_win integer
|
||||
---@field expected_win integer
|
||||
|
||||
---@class EditState
|
||||
---@field test_buffers TestBufferPair[]
|
||||
---@field test_cases TestCase[]
|
||||
---@field constraints ProblemConstraints?
|
||||
|
||||
---@type EditState?
|
||||
local edit_state = nil
|
||||
|
||||
local function setup_keybindings(buf)
|
||||
vim.keymap.set('n', 'q', function()
|
||||
M.toggle_edit()
|
||||
end, { buffer = buf, silent = true, desc = 'Save and exit test editor' })
|
||||
end
|
||||
|
||||
local function load_test_into_buffer(test_index)
|
||||
if not edit_state then
|
||||
return
|
||||
end
|
||||
|
||||
local tc = edit_state.test_cases[test_index]
|
||||
local pair = edit_state.test_buffers[test_index]
|
||||
|
||||
if not tc or not pair then
|
||||
return
|
||||
end
|
||||
|
||||
local input_lines = vim.split(tc.input or '', '\n', { plain = true, trimempty = false })
|
||||
vim.api.nvim_buf_set_lines(pair.input_buf, 0, -1, false, input_lines)
|
||||
|
||||
local expected_lines = vim.split(tc.expected or '', '\n', { plain = true, trimempty = false })
|
||||
vim.api.nvim_buf_set_lines(pair.expected_buf, 0, -1, false, expected_lines)
|
||||
|
||||
vim.api.nvim_buf_set_name(pair.input_buf, string.format('cp://test-%d-input', test_index))
|
||||
vim.api.nvim_buf_set_name(pair.expected_buf, string.format('cp://test-%d-expected', test_index))
|
||||
end
|
||||
|
||||
local function save_all_tests()
|
||||
if not edit_state then
|
||||
return
|
||||
end
|
||||
|
||||
local platform = state.get_platform()
|
||||
local contest_id = state.get_contest_id()
|
||||
local problem_id = state.get_problem_id()
|
||||
|
||||
if not platform or not contest_id or not problem_id then
|
||||
return
|
||||
end
|
||||
|
||||
for i, pair in ipairs(edit_state.test_buffers) do
|
||||
if
|
||||
vim.api.nvim_buf_is_valid(pair.input_buf) and vim.api.nvim_buf_is_valid(pair.expected_buf)
|
||||
then
|
||||
local input_lines = vim.api.nvim_buf_get_lines(pair.input_buf, 0, -1, false)
|
||||
local expected_lines = vim.api.nvim_buf_get_lines(pair.expected_buf, 0, -1, false)
|
||||
|
||||
edit_state.test_cases[i].input = table.concat(input_lines, '\n')
|
||||
edit_state.test_cases[i].expected = table.concat(expected_lines, '\n')
|
||||
end
|
||||
end
|
||||
|
||||
cache.set_test_cases(
|
||||
platform,
|
||||
contest_id,
|
||||
problem_id,
|
||||
edit_state.test_cases,
|
||||
edit_state.constraints and edit_state.constraints.timeout_ms or 0,
|
||||
edit_state.constraints and edit_state.constraints.memory_mb or 0,
|
||||
false
|
||||
)
|
||||
|
||||
local config = config_module.get_config()
|
||||
local base_name = config.filename and config.filename(platform, contest_id, problem_id, config)
|
||||
or config_module.default_filename(contest_id, problem_id)
|
||||
|
||||
vim.fn.mkdir('io', 'p')
|
||||
|
||||
for i, tc in ipairs(edit_state.test_cases) do
|
||||
local input_file = string.format('io/%s.%d.cpin', base_name, i)
|
||||
local expected_file = string.format('io/%s.%d.cpout', base_name, i)
|
||||
|
||||
local input_content = (tc.input or ''):gsub('\r', '')
|
||||
local expected_content = (tc.expected or ''):gsub('\r', '')
|
||||
|
||||
vim.fn.writefile(vim.split(input_content, '\n', { trimempty = true }), input_file)
|
||||
vim.fn.writefile(vim.split(expected_content, '\n', { trimempty = true }), expected_file)
|
||||
end
|
||||
|
||||
logger.log('Saved all test cases')
|
||||
end
|
||||
|
||||
function M.toggle_edit(test_index)
|
||||
if edit_state then
|
||||
save_all_tests()
|
||||
edit_state = nil
|
||||
|
||||
local saved = state.get_saved_session()
|
||||
if saved then
|
||||
vim.fn.delete(saved)
|
||||
state.set_saved_session(nil)
|
||||
end
|
||||
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
local source_file = state.get_source_file()
|
||||
if source_file and vim.fn.filereadable(source_file) == 1 then
|
||||
vim.cmd.edit(source_file)
|
||||
end
|
||||
|
||||
local views = require('cp.ui.views')
|
||||
views.ensure_io_view()
|
||||
|
||||
logger.log('Closed test editor')
|
||||
return
|
||||
end
|
||||
|
||||
local platform, contest_id, problem_id =
|
||||
state.get_platform(), state.get_contest_id(), state.get_problem_id()
|
||||
|
||||
if not platform or not contest_id or not problem_id then
|
||||
logger.log('No problem context. Run :CP <platform> <contest> first.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
cache.load()
|
||||
local test_cases = cache.get_test_cases(platform, contest_id, problem_id)
|
||||
|
||||
if not test_cases or #test_cases == 0 then
|
||||
logger.log('No test cases available for editing.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local timeout_ms, memory_mb = cache.get_constraints(platform, contest_id, problem_id)
|
||||
local constraints = (timeout_ms and memory_mb)
|
||||
and { timeout_ms = timeout_ms, memory_mb = memory_mb }
|
||||
or nil
|
||||
|
||||
local target_index = test_index or 1
|
||||
if target_index < 1 or target_index > #test_cases then
|
||||
logger.log(
|
||||
('Test %d does not exist (only %d tests available)'):format(target_index, #test_cases),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local io_view_state = state.get_io_view_state()
|
||||
if io_view_state then
|
||||
if io_view_state.output_buf and vim.api.nvim_buf_is_valid(io_view_state.output_buf) then
|
||||
vim.api.nvim_buf_delete(io_view_state.output_buf, { force = true })
|
||||
end
|
||||
if io_view_state.input_buf and vim.api.nvim_buf_is_valid(io_view_state.input_buf) then
|
||||
vim.api.nvim_buf_delete(io_view_state.input_buf, { force = true })
|
||||
end
|
||||
state.set_io_view_state(nil)
|
||||
end
|
||||
|
||||
local session_file = vim.fn.tempname()
|
||||
state.set_saved_session(session_file)
|
||||
-- selene: allow(mixed_table)
|
||||
vim.cmd.mksession({ session_file, bang = true })
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
|
||||
local test_buffers = {}
|
||||
local num_tests = #test_cases
|
||||
|
||||
for _ = 1, num_tests - 1 do
|
||||
vim.cmd.vsplit()
|
||||
end
|
||||
|
||||
vim.cmd('1 wincmd w')
|
||||
|
||||
for col = 1, num_tests do
|
||||
vim.cmd.split()
|
||||
|
||||
vim.cmd.wincmd('k')
|
||||
local input_win = vim.api.nvim_get_current_win()
|
||||
local input_buf = utils.create_buffer_with_options()
|
||||
vim.api.nvim_win_set_buf(input_win, input_buf)
|
||||
vim.bo[input_buf].modifiable = true
|
||||
vim.bo[input_buf].readonly = false
|
||||
vim.bo[input_buf].buftype = 'nofile'
|
||||
vim.bo[input_buf].buflisted = false
|
||||
helpers.clearcol(input_buf)
|
||||
|
||||
vim.cmd.wincmd('j')
|
||||
local expected_win = vim.api.nvim_get_current_win()
|
||||
local expected_buf = utils.create_buffer_with_options()
|
||||
vim.api.nvim_win_set_buf(expected_win, expected_buf)
|
||||
vim.bo[expected_buf].modifiable = true
|
||||
vim.bo[expected_buf].readonly = false
|
||||
vim.bo[expected_buf].buftype = 'nofile'
|
||||
vim.bo[expected_buf].buflisted = false
|
||||
helpers.clearcol(expected_buf)
|
||||
|
||||
test_buffers[col] = {
|
||||
input_buf = input_buf,
|
||||
expected_buf = expected_buf,
|
||||
input_win = input_win,
|
||||
expected_win = expected_win,
|
||||
}
|
||||
|
||||
vim.cmd.wincmd('k')
|
||||
vim.cmd.wincmd('l')
|
||||
end
|
||||
|
||||
edit_state = {
|
||||
test_buffers = test_buffers,
|
||||
test_cases = test_cases,
|
||||
constraints = constraints,
|
||||
}
|
||||
|
||||
for i = 1, num_tests do
|
||||
load_test_into_buffer(i)
|
||||
end
|
||||
|
||||
for _, pair in ipairs(test_buffers) do
|
||||
setup_keybindings(pair.input_buf)
|
||||
setup_keybindings(pair.expected_buf)
|
||||
end
|
||||
|
||||
if
|
||||
test_buffers[target_index]
|
||||
and vim.api.nvim_win_is_valid(test_buffers[target_index].input_win)
|
||||
then
|
||||
vim.api.nvim_set_current_win(test_buffers[target_index].input_win)
|
||||
end
|
||||
|
||||
logger.log(('Editing %d test cases'):format(num_tests))
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -11,7 +11,7 @@ local function create_none_diff_layout(parent_win, expected_content, actual_cont
|
|||
|
||||
vim.api.nvim_set_current_win(parent_win)
|
||||
vim.cmd.split()
|
||||
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
||||
vim.cmd.resize(math.floor(vim.o.lines * 0.35))
|
||||
local actual_win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(actual_win, actual_buf)
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ local function create_vim_diff_layout(parent_win, expected_content, actual_conte
|
|||
|
||||
vim.api.nvim_set_current_win(parent_win)
|
||||
vim.cmd.split()
|
||||
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
||||
vim.cmd.resize(math.floor(vim.o.lines * 0.35))
|
||||
local actual_win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(actual_win, actual_buf)
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ local function create_git_diff_layout(parent_win, expected_content, actual_conte
|
|||
|
||||
vim.api.nvim_set_current_win(parent_win)
|
||||
vim.cmd.split()
|
||||
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
||||
vim.cmd.resize(math.floor(vim.o.lines * 0.35))
|
||||
local diff_win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(diff_win, diff_buf)
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ local function create_single_layout(parent_win, content)
|
|||
|
||||
vim.api.nvim_set_current_win(parent_win)
|
||||
vim.cmd.split()
|
||||
vim.cmd('resize ' .. math.floor(vim.o.lines * 0.35))
|
||||
vim.cmd.resize(math.floor(vim.o.lines * 0.35))
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(win, buf)
|
||||
vim.api.nvim_set_option_value('filetype', 'cp', { buf = buf })
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ function M.toggle_interactive(interactor_cmd)
|
|||
end
|
||||
end
|
||||
if state.saved_interactive_session then
|
||||
vim.cmd(('source %s'):format(state.saved_interactive_session))
|
||||
vim.cmd.source(state.saved_interactive_session)
|
||||
vim.fn.delete(state.saved_interactive_session)
|
||||
state.saved_interactive_session = nil
|
||||
end
|
||||
|
|
@ -75,8 +75,9 @@ function M.toggle_interactive(interactor_cmd)
|
|||
end
|
||||
|
||||
state.saved_interactive_session = vim.fn.tempname()
|
||||
vim.cmd(('mksession! %s'):format(state.saved_interactive_session))
|
||||
vim.cmd('silent only')
|
||||
-- selene: allow(mixed_table)
|
||||
vim.cmd.mksession({ state.saved_interactive_session, bang = true })
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
|
||||
local execute = require('cp.runner.execute')
|
||||
local run = require('cp.runner.run')
|
||||
|
|
@ -104,7 +105,7 @@ function M.toggle_interactive(interactor_cmd)
|
|||
vim.log.levels.ERROR
|
||||
)
|
||||
if state.saved_interactive_session then
|
||||
vim.cmd(('source %s'):format(state.saved_interactive_session))
|
||||
vim.cmd.source(state.saved_interactive_session)
|
||||
vim.fn.delete(state.saved_interactive_session)
|
||||
state.saved_interactive_session = nil
|
||||
end
|
||||
|
|
@ -122,7 +123,7 @@ function M.toggle_interactive(interactor_cmd)
|
|||
cmdline = vim.fn.shellescape(binary)
|
||||
end
|
||||
|
||||
vim.cmd('terminal ' .. cmdline)
|
||||
vim.cmd.terminal(cmdline)
|
||||
local term_buf = vim.api.nvim_get_current_buf()
|
||||
local term_win = vim.api.nvim_get_current_win()
|
||||
|
||||
|
|
@ -139,7 +140,7 @@ function M.toggle_interactive(interactor_cmd)
|
|||
end
|
||||
end
|
||||
if state.saved_interactive_session then
|
||||
vim.cmd(('source %s'):format(state.saved_interactive_session))
|
||||
vim.cmd.source(state.saved_interactive_session)
|
||||
vim.fn.delete(state.saved_interactive_session)
|
||||
state.saved_interactive_session = nil
|
||||
end
|
||||
|
|
@ -247,6 +248,23 @@ function M.ensure_io_view()
|
|||
current_test_index = 1,
|
||||
})
|
||||
|
||||
local source_buf = vim.api.nvim_win_get_buf(solution_win)
|
||||
vim.api.nvim_create_autocmd('BufDelete', {
|
||||
buffer = source_buf,
|
||||
callback = function()
|
||||
local io = state.get_io_view_state()
|
||||
if io then
|
||||
if io.output_buf and vim.api.nvim_buf_is_valid(io.output_buf) then
|
||||
vim.api.nvim_buf_delete(io.output_buf, { force = true })
|
||||
end
|
||||
if io.input_buf and vim.api.nvim_buf_is_valid(io.input_buf) then
|
||||
vim.api.nvim_buf_delete(io.input_buf, { force = true })
|
||||
end
|
||||
state.set_io_view_state(nil)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
if cfg.hooks and cfg.hooks.setup_io_output then
|
||||
pcall(cfg.hooks.setup_io_output, output_buf, state)
|
||||
end
|
||||
|
|
@ -400,6 +418,25 @@ function M.run_io_view(test_index, debug)
|
|||
|
||||
local formatter = config.ui.run.format_verdict
|
||||
|
||||
local max_time_actual = 0
|
||||
local max_time_limit = 0
|
||||
local max_mem_actual = 0
|
||||
local max_mem_limit = 0
|
||||
|
||||
for _, idx in ipairs(test_indices) do
|
||||
local tc = test_state.test_cases[idx]
|
||||
max_time_actual = math.max(max_time_actual, #string.format('%.2f', tc.time_ms or 0))
|
||||
max_time_limit = math.max(
|
||||
max_time_limit,
|
||||
#tostring(test_state.constraints and test_state.constraints.timeout_ms or 0)
|
||||
)
|
||||
max_mem_actual = math.max(max_mem_actual, #string.format('%.0f', tc.rss_mb or 0))
|
||||
max_mem_limit = math.max(
|
||||
max_mem_limit,
|
||||
#string.format('%.0f', test_state.constraints and test_state.constraints.memory_mb or 0)
|
||||
)
|
||||
end
|
||||
|
||||
for _, idx in ipairs(test_indices) do
|
||||
local tc = test_state.test_cases[idx]
|
||||
|
||||
|
|
@ -425,6 +462,10 @@ function M.run_io_view(test_index, debug)
|
|||
exit_code = tc.code or 0,
|
||||
signal = (tc.code and tc.code >= 128) and require('cp.constants').signal_codes[tc.code]
|
||||
or nil,
|
||||
time_actual_width = max_time_actual,
|
||||
time_limit_width = max_time_limit,
|
||||
mem_actual_width = max_mem_actual,
|
||||
mem_limit_width = max_mem_limit,
|
||||
}
|
||||
|
||||
local result = formatter(format_data)
|
||||
|
|
@ -484,7 +525,7 @@ function M.toggle_panel(panel_opts)
|
|||
end
|
||||
local saved = state.get_saved_session()
|
||||
if saved then
|
||||
vim.cmd(('source %s'):format(saved))
|
||||
vim.cmd.source(saved)
|
||||
vim.fn.delete(saved)
|
||||
state.set_saved_session(nil)
|
||||
end
|
||||
|
|
@ -542,8 +583,9 @@ function M.toggle_panel(panel_opts)
|
|||
|
||||
local session_file = vim.fn.tempname()
|
||||
state.set_saved_session(session_file)
|
||||
vim.cmd(('mksession! %s'):format(session_file))
|
||||
vim.cmd('silent only')
|
||||
-- selene: allow(mixed_table)
|
||||
vim.cmd.mksession({ session_file, bang = true })
|
||||
vim.cmd.only({ mods = { silent = true } })
|
||||
|
||||
local tab_buf = utils.create_buffer_with_options()
|
||||
helpers.clearcol(tab_buf)
|
||||
|
|
@ -589,8 +631,9 @@ function M.toggle_panel(panel_opts)
|
|||
and vim.api.nvim_win_is_valid(test_windows.tab_win)
|
||||
then
|
||||
vim.api.nvim_win_set_cursor(test_windows.tab_win, { current_line, 0 })
|
||||
-- selene: allow(mixed_table)
|
||||
vim.api.nvim_win_call(test_windows.tab_win, function()
|
||||
vim.cmd('normal! zz')
|
||||
vim.cmd.normal({ 'zz', bang = true })
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -69,6 +69,23 @@ end, {
|
|||
elseif args[2] == 'interact' then
|
||||
local utils = require('cp.utils')
|
||||
return filter_candidates(utils.cwd_executables())
|
||||
elseif args[2] == 'edit' then
|
||||
local state = require('cp.state')
|
||||
local platform = state.get_platform()
|
||||
local contest_id = state.get_contest_id()
|
||||
local problem_id = state.get_problem_id()
|
||||
local candidates = {}
|
||||
if platform and contest_id and problem_id then
|
||||
local cache = require('cp.cache')
|
||||
cache.load()
|
||||
local test_cases = cache.get_test_cases(platform, contest_id, problem_id)
|
||||
if test_cases then
|
||||
for i = 1, #test_cases do
|
||||
table.insert(candidates, tostring(i))
|
||||
end
|
||||
end
|
||||
end
|
||||
return filter_candidates(candidates)
|
||||
elseif args[2] == 'run' or args[2] == 'panel' then
|
||||
local state = require('cp.state')
|
||||
local platform = state.get_platform()
|
||||
|
|
@ -96,7 +113,9 @@ end, {
|
|||
end
|
||||
elseif num_args == 4 then
|
||||
if args[2] == 'cache' and args[3] == 'clear' then
|
||||
return filter_candidates(platforms)
|
||||
local candidates = vim.list_extend({}, platforms)
|
||||
table.insert(candidates, '')
|
||||
return filter_candidates(candidates)
|
||||
elseif args[3] == '--lang' then
|
||||
local platform = require('cp.state').get_platform()
|
||||
return filter_candidates(get_enabled_languages(platform))
|
||||
|
|
@ -115,7 +134,12 @@ end, {
|
|||
return filter_candidates(candidates)
|
||||
end
|
||||
elseif num_args == 5 then
|
||||
if vim.tbl_contains(platforms, args[2]) then
|
||||
if args[2] == 'cache' and args[3] == 'clear' and vim.tbl_contains(platforms, args[4]) then
|
||||
local cache = require('cp.cache')
|
||||
cache.load()
|
||||
local contests = cache.get_cached_contest_ids(args[4])
|
||||
return filter_candidates(contests)
|
||||
elseif vim.tbl_contains(platforms, args[2]) then
|
||||
if args[4] == '--lang' then
|
||||
return filter_candidates(get_enabled_languages(args[2]))
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue