feat(ui): io view
This commit is contained in:
parent
347be72774
commit
ad17855532
2 changed files with 192 additions and 22 deletions
|
|
@ -3,9 +3,15 @@
|
|||
---@field platform string
|
||||
---@field contest_id string
|
||||
---@field language string
|
||||
---@field requested_problem_id string|nil
|
||||
---@field requested_problem_id string?
|
||||
---@field token integer
|
||||
|
||||
---@class cp.IoViewState
|
||||
---@field output_buf integer
|
||||
---@field input_buf integer
|
||||
---@field output_win integer
|
||||
---@field input_win integer
|
||||
|
||||
---@class cp.State
|
||||
---@field get_platform fun(): string?
|
||||
---@field set_platform fun(platform: string)
|
||||
|
|
@ -21,8 +27,12 @@
|
|||
---@field get_input_file fun(): string?
|
||||
---@field get_output_file fun(): string?
|
||||
---@field get_expected_file fun(): string?
|
||||
---@field get_provisional fun(): cp.ProvisionalState|nil
|
||||
---@field set_provisional fun(p: cp.ProvisionalState|nil)
|
||||
---@field get_provisional fun(): cp.ProvisionalState?
|
||||
---@field set_provisional fun(p: cp.ProvisionalState?)
|
||||
---@field get_saved_session fun(): string?
|
||||
---@field set_saved_session fun(path: string?)
|
||||
---@field get_io_view_state fun(): cp.IoViewState?
|
||||
---@field set_io_view_state fun(s: cp.IoViewState?)
|
||||
|
||||
local M = {}
|
||||
|
||||
|
|
@ -36,9 +46,10 @@ local state = {
|
|||
active_panel = nil,
|
||||
provisional = nil,
|
||||
solution_win = nil,
|
||||
io_view_state = nil,
|
||||
}
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_platform()
|
||||
return state.platform
|
||||
end
|
||||
|
|
@ -48,7 +59,7 @@ function M.set_platform(platform)
|
|||
state.platform = platform
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_contest_id()
|
||||
return state.contest_id
|
||||
end
|
||||
|
|
@ -58,7 +69,7 @@ function M.set_contest_id(contest_id)
|
|||
state.contest_id = contest_id
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_problem_id()
|
||||
return state.problem_id
|
||||
end
|
||||
|
|
@ -68,7 +79,7 @@ function M.set_problem_id(problem_id)
|
|||
state.problem_id = problem_id
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_base_name()
|
||||
local platform, contest_id, problem_id = M.get_platform(), M.get_contest_id(), M.get_problem_id()
|
||||
if not platform or not contest_id or not problem_id then
|
||||
|
|
@ -86,7 +97,7 @@ function M.get_base_name()
|
|||
end
|
||||
|
||||
---@param language? string
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_source_file(language)
|
||||
local base_name = M.get_base_name()
|
||||
if not base_name or not M.get_platform() then
|
||||
|
|
@ -110,46 +121,46 @@ function M.get_source_file(language)
|
|||
return base_name .. '.' .. eff.extension
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_binary_file()
|
||||
local base_name = M.get_base_name()
|
||||
return base_name and ('build/%s.run'):format(base_name) or nil
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_input_file()
|
||||
local base_name = M.get_base_name()
|
||||
return base_name and ('io/%s.cpin'):format(base_name) or nil
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_output_file()
|
||||
local base_name = M.get_base_name()
|
||||
return base_name and ('io/%s.cpout'):format(base_name) or nil
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_expected_file()
|
||||
local base_name = M.get_base_name()
|
||||
return base_name and ('io/%s.expected'):format(base_name) or nil
|
||||
end
|
||||
|
||||
---@return string|nil
|
||||
---@return string?
|
||||
function M.get_active_panel()
|
||||
return state.active_panel
|
||||
end
|
||||
|
||||
---@param panel string|nil
|
||||
---@param panel string?
|
||||
function M.set_active_panel(panel)
|
||||
state.active_panel = panel
|
||||
end
|
||||
|
||||
---@return cp.ProvisionalState|nil
|
||||
---@return cp.ProvisionalState?
|
||||
function M.get_provisional()
|
||||
return state.provisional
|
||||
end
|
||||
|
||||
---@param p cp.ProvisionalState|nil
|
||||
---@param p cp.ProvisionalState?
|
||||
function M.set_provisional(p)
|
||||
state.provisional = p
|
||||
end
|
||||
|
|
@ -167,6 +178,38 @@ function M.set_solution_win(win)
|
|||
state.solution_win = win
|
||||
end
|
||||
|
||||
---@return cp.IoViewState?
|
||||
function M.get_io_view_state()
|
||||
if not state.io_view_state then
|
||||
return nil
|
||||
end
|
||||
local s = state.io_view_state
|
||||
if
|
||||
vim.api.nvim_buf_is_valid(s.output_buf)
|
||||
and vim.api.nvim_buf_is_valid(s.input_buf)
|
||||
and vim.api.nvim_win_is_valid(s.output_win)
|
||||
and vim.api.nvim_win_is_valid(s.input_win)
|
||||
then
|
||||
return s
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param s cp.IoViewState?
|
||||
function M.set_io_view_state(s)
|
||||
state.io_view_state = s
|
||||
end
|
||||
|
||||
---@return string?
|
||||
function M.get_saved_session()
|
||||
return state.saved_session
|
||||
end
|
||||
|
||||
---@param path string?
|
||||
function M.set_saved_session(path)
|
||||
state.saved_session = path
|
||||
end
|
||||
|
||||
M._state = state
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -204,6 +204,131 @@ function M.toggle_interactive(interactor_cmd)
|
|||
state.set_active_panel('interactive')
|
||||
end
|
||||
|
||||
function M.toggle_io_view()
|
||||
local io_state = state.get_io_view_state()
|
||||
if io_state then
|
||||
if vim.api.nvim_buf_is_valid(io_state.output_buf) then
|
||||
vim.api.nvim_buf_delete(io_state.output_buf, { force = true })
|
||||
end
|
||||
if vim.api.nvim_buf_is_valid(io_state.input_buf) then
|
||||
vim.api.nvim_buf_delete(io_state.input_buf, { force = true })
|
||||
end
|
||||
state.set_io_view_state(nil)
|
||||
return
|
||||
end
|
||||
|
||||
local platform, contest_id = state.get_platform(), state.get_contest_id()
|
||||
if not platform then
|
||||
logger.log(
|
||||
'No platform configured. Use :CP <platform> <contest> [...] first.',
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
if not contest_id then
|
||||
logger.log(
|
||||
("No contest '%s' configured for platform '%s'."):format(
|
||||
contest_id,
|
||||
constants.PLATFORM_DISPLAY_NAMES[platform]
|
||||
),
|
||||
vim.log.levels.ERROR
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
local problem_id = state.get_problem_id()
|
||||
if not problem_id then
|
||||
logger.log('No problem is active.', vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local cache = require('cp.cache')
|
||||
cache.load()
|
||||
local contest_data = cache.get_contest_data(platform, contest_id)
|
||||
if
|
||||
contest_data
|
||||
and contest_data.index_map
|
||||
and contest_data.problems[contest_data.index_map[problem_id]]
|
||||
and contest_data.problems[contest_data.index_map[problem_id]].interactive
|
||||
then
|
||||
logger.log('This is an interactive problem. Use :CP interact instead.', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local run = require('cp.runner.run')
|
||||
if not run.load_test_cases() then
|
||||
logger.log('no test cases found', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
local execute = require('cp.runner.execute')
|
||||
local compile_result = execute.compile_problem()
|
||||
if compile_result.success then
|
||||
run.run_all_test_cases()
|
||||
else
|
||||
run.handle_compilation_failure(compile_result.output)
|
||||
end
|
||||
|
||||
local solution_win = state.get_solution_win()
|
||||
vim.api.nvim_set_current_win(solution_win)
|
||||
|
||||
vim.cmd.vsplit()
|
||||
local output_win = vim.api.nvim_get_current_win()
|
||||
local output_buf = utils.create_buffer_with_options()
|
||||
vim.api.nvim_win_set_buf(output_win, output_buf)
|
||||
|
||||
vim.cmd.split()
|
||||
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)
|
||||
|
||||
local test_state = run.get_panel_state()
|
||||
local run_render = require('cp.runner.run_render')
|
||||
run_render.setup_highlights()
|
||||
|
||||
local verdict_lines = {}
|
||||
local verdict_highlights = {}
|
||||
for i, tc in ipairs(test_state.test_cases) do
|
||||
local status = run_render.get_status_info(tc)
|
||||
local time = tc.time_ms and string.format('%.2f', tc.time_ms) or '—'
|
||||
local mem = tc.rss_mb and string.format('%.0f', tc.rss_mb) or '—'
|
||||
local line = string.format('Test %d: %s (%sms, %sMB)', i, status.text, time, mem)
|
||||
table.insert(verdict_lines, line)
|
||||
local status_pos = line:find(status.text, 1, true)
|
||||
if status_pos then
|
||||
table.insert(verdict_highlights, {
|
||||
line = i - 1,
|
||||
col_start = status_pos - 1,
|
||||
col_end = status_pos - 1 + #status.text,
|
||||
highlight_group = status.highlight_group,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local verdict_ns = vim.api.nvim_create_namespace('cp_io_view_verdict')
|
||||
utils.update_buffer_content(output_buf, verdict_lines, verdict_highlights, verdict_ns)
|
||||
|
||||
local hint_lines = { 'Multiple tests running...', 'Use :CP run <n> to view specific test' }
|
||||
utils.update_buffer_content(input_buf, hint_lines, nil, nil)
|
||||
|
||||
vim.keymap.set('n', 'q', function()
|
||||
M.toggle_io_view()
|
||||
end, { buffer = output_buf, silent = true })
|
||||
vim.keymap.set('n', 'q', function()
|
||||
M.toggle_io_view()
|
||||
end, { buffer = input_buf, silent = true })
|
||||
|
||||
state.set_io_view_state({
|
||||
output_buf = output_buf,
|
||||
input_buf = input_buf,
|
||||
output_win = output_win,
|
||||
input_win = input_win,
|
||||
})
|
||||
|
||||
vim.api.nvim_set_current_win(output_win)
|
||||
end
|
||||
|
||||
---@param panel_opts? PanelOpts
|
||||
function M.toggle_panel(panel_opts)
|
||||
if state.get_active_panel() == 'run' then
|
||||
|
|
@ -212,10 +337,11 @@ function M.toggle_panel(panel_opts)
|
|||
current_diff_layout = nil
|
||||
current_mode = nil
|
||||
end
|
||||
if state.saved_session then
|
||||
vim.cmd(('source %s'):format(state.saved_session))
|
||||
vim.fn.delete(state.saved_session)
|
||||
state.saved_session = nil
|
||||
local saved = state.get_saved_session()
|
||||
if saved then
|
||||
vim.cmd(('source %s'):format(saved))
|
||||
vim.fn.delete(saved)
|
||||
state.set_saved_session(nil)
|
||||
end
|
||||
state.set_active_panel(nil)
|
||||
return
|
||||
|
|
@ -268,8 +394,9 @@ function M.toggle_panel(panel_opts)
|
|||
return
|
||||
end
|
||||
|
||||
state.saved_session = vim.fn.tempname()
|
||||
vim.cmd(('mksession! %s'):format(state.saved_session))
|
||||
local session_file = vim.fn.tempname()
|
||||
state.set_saved_session(session_file)
|
||||
vim.cmd(('mksession! %s'):format(session_file))
|
||||
vim.cmd('silent only')
|
||||
|
||||
local tab_buf = utils.create_buffer_with_options()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue