commit
83645b48be
7 changed files with 109 additions and 54 deletions
|
|
@ -152,7 +152,7 @@ Here's an example configuration with lazy.nvim: >lua
|
||||||
diff_mode = 'vim',
|
diff_mode = 'vim',
|
||||||
next_test_key = '<c-n>',
|
next_test_key = '<c-n>',
|
||||||
prev_test_key = '<c-p>',
|
prev_test_key = '<c-p>',
|
||||||
toggle_diff_key = 't',
|
toggle_diff_key = '<c-q>',
|
||||||
max_output_lines = 50,
|
max_output_lines = 50,
|
||||||
},
|
},
|
||||||
diff = {
|
diff = {
|
||||||
|
|
@ -214,7 +214,8 @@ Here's an example configuration with lazy.nvim: >lua
|
||||||
"vim" uses built-in diff, "git" provides character-level precision.
|
"vim" uses built-in diff, "git" provides character-level precision.
|
||||||
{next_test_key} (string, default: "<c-n>") Key to navigate to next test case.
|
{next_test_key} (string, default: "<c-n>") Key to navigate to next test case.
|
||||||
{prev_test_key} (string, default: "<c-p>") Key to navigate to previous test case.
|
{prev_test_key} (string, default: "<c-p>") Key to navigate to previous test case.
|
||||||
{toggle_diff_key} (string, default: "t") Key to cycle through diff modes.
|
{toggle_diff_key} (string, default: "<c-t>") Key to cycle through diff modes.
|
||||||
|
{close_key} (string, default: "<c-q>") Close the run panel/interactive terminal
|
||||||
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
{max_output_lines} (number, default: 50) Maximum lines of test output.
|
||||||
|
|
||||||
*cp.DiffConfig*
|
*cp.DiffConfig*
|
||||||
|
|
@ -531,9 +532,9 @@ RUN PANEL KEYMAPS *cp-test-keys*
|
||||||
run_panel.next_test_key)
|
run_panel.next_test_key)
|
||||||
<c-p> Navigate to previous test case (configurable via
|
<c-p> Navigate to previous test case (configurable via
|
||||||
run_panel.prev_test_key)
|
run_panel.prev_test_key)
|
||||||
t Cycle through diff modes: none → git → vim (configurable
|
<c-t> Cycle through diff modes: none → git → vim (configurable
|
||||||
via run_panel.toggle_diff_key)
|
via run_panel.toggle_diff_key)
|
||||||
q Exit test panel and restore layout
|
<c-q> Exit run panel/interactive terminal and restore layout
|
||||||
|
|
||||||
Diff Modes ~
|
Diff Modes ~
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@ function M.handle_command(opts)
|
||||||
local setup = require('cp.setup')
|
local setup = require('cp.setup')
|
||||||
local ui = require('cp.ui.panel')
|
local ui = require('cp.ui.panel')
|
||||||
|
|
||||||
if cmd.action == 'run' then
|
if cmd.action == 'interact' then
|
||||||
|
ui.toggle_interactive()
|
||||||
|
elseif cmd.action == 'run' then
|
||||||
ui.toggle_run_panel(cmd.debug)
|
ui.toggle_run_panel(cmd.debug)
|
||||||
elseif cmd.action == 'next' then
|
elseif cmd.action == 'next' then
|
||||||
setup.navigate_problem(1, cmd.language)
|
setup.navigate_problem(1, cmd.language)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
---@field next_test_key string Key to navigate to next test case
|
---@field next_test_key string Key to navigate to next test case
|
||||||
---@field prev_test_key string Key to navigate to previous test case
|
---@field prev_test_key string Key to navigate to previous test case
|
||||||
---@field toggle_diff_key string Key to cycle through diff modes
|
---@field toggle_diff_key string Key to cycle through diff modes
|
||||||
|
---@field close_key string Key to close panel/interactive terminal
|
||||||
---@field max_output_lines number Maximum lines of test output to display
|
---@field max_output_lines number Maximum lines of test output to display
|
||||||
|
|
||||||
---@class DiffGitConfig
|
---@class DiffGitConfig
|
||||||
|
|
@ -103,7 +104,8 @@ M.defaults = {
|
||||||
diff_mode = 'none',
|
diff_mode = 'none',
|
||||||
next_test_key = '<c-n>',
|
next_test_key = '<c-n>',
|
||||||
prev_test_key = '<c-p>',
|
prev_test_key = '<c-p>',
|
||||||
toggle_diff_key = 't',
|
toggle_diff_key = '<c-t>',
|
||||||
|
close_key = '<c-q>',
|
||||||
max_output_lines = 50,
|
max_output_lines = 50,
|
||||||
},
|
},
|
||||||
diff = {
|
diff = {
|
||||||
|
|
@ -229,6 +231,13 @@ function M.setup(user_config)
|
||||||
end,
|
end,
|
||||||
'toggle_diff_key must be a non-empty string',
|
'toggle_diff_key must be a non-empty string',
|
||||||
},
|
},
|
||||||
|
close_key = {
|
||||||
|
config.run_panel.close_key,
|
||||||
|
function(value)
|
||||||
|
return type(value) == 'string' and value ~= ''
|
||||||
|
end,
|
||||||
|
'close_key must be a non-empty string',
|
||||||
|
},
|
||||||
max_output_lines = {
|
max_output_lines = {
|
||||||
config.run_panel.max_output_lines,
|
config.run_panel.max_output_lines,
|
||||||
function(value)
|
function(value)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
|
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
|
||||||
M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache' }
|
M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache', 'interact' }
|
||||||
|
|
||||||
M.PLATFORM_DISPLAY_NAMES = {
|
M.PLATFORM_DISPLAY_NAMES = {
|
||||||
atcoder = 'AtCoder',
|
atcoder = 'AtCoder',
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
---@field set_problem_id fun(problem_id: string)
|
---@field set_problem_id fun(problem_id: string)
|
||||||
---@field get_test_cases fun(): table[]?
|
---@field get_test_cases fun(): table[]?
|
||||||
---@field set_test_cases fun(test_cases: table[])
|
---@field set_test_cases fun(test_cases: table[])
|
||||||
---@field is_run_panel_active fun(): boolean
|
|
||||||
---@field set_run_panel_active fun(active: boolean)
|
|
||||||
---@field get_saved_session fun(): table?
|
---@field get_saved_session fun(): table?
|
||||||
---@field set_saved_session fun(session: table)
|
---@field set_saved_session fun(session: table)
|
||||||
---@field get_context fun(): {platform: string?, contest_id: string?, problem_id: string?}
|
---@field get_context fun(): {platform: string?, contest_id: string?, problem_id: string?}
|
||||||
|
|
@ -28,8 +26,8 @@ local state = {
|
||||||
contest_id = nil,
|
contest_id = nil,
|
||||||
problem_id = nil,
|
problem_id = nil,
|
||||||
test_cases = nil,
|
test_cases = nil,
|
||||||
run_panel_active = false,
|
|
||||||
saved_session = nil,
|
saved_session = nil,
|
||||||
|
active_panel = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
function M.get_platform()
|
function M.get_platform()
|
||||||
|
|
@ -64,14 +62,6 @@ function M.set_test_cases(test_cases)
|
||||||
state.test_cases = test_cases
|
state.test_cases = test_cases
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.is_run_panel_active()
|
|
||||||
return state.run_panel_active
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.set_run_panel_active(active)
|
|
||||||
state.run_panel_active = active
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_saved_session()
|
function M.get_saved_session()
|
||||||
return state.saved_session
|
return state.saved_session
|
||||||
end
|
end
|
||||||
|
|
@ -149,6 +139,14 @@ function M.has_context()
|
||||||
return state.platform and state.contest_id
|
return state.platform and state.contest_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.get_active_panel()
|
||||||
|
return state.active_panel
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.set_active_panel(panel)
|
||||||
|
state.active_panel = panel
|
||||||
|
end
|
||||||
|
|
||||||
function M.reset()
|
function M.reset()
|
||||||
state.platform = nil
|
state.platform = nil
|
||||||
state.contest_id = nil
|
state.contest_id = nil
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,66 @@ local state = require('cp.state')
|
||||||
local current_diff_layout = nil
|
local current_diff_layout = nil
|
||||||
local current_mode = nil
|
local current_mode = nil
|
||||||
|
|
||||||
|
function M.toggle_interactive()
|
||||||
|
if state.get_active_panel() == 'interactive' then
|
||||||
|
if state.interactive_buf and vim.api.nvim_buf_is_valid(state.interactive_buf) then
|
||||||
|
local job = vim.b[state.interactive_buf].terminal_job_id
|
||||||
|
if job then
|
||||||
|
vim.fn.jobstop(job)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if state.saved_interactive_session then
|
||||||
|
vim.cmd(('source %s'):format(state.saved_interactive_session))
|
||||||
|
vim.fn.delete(state.saved_interactive_session)
|
||||||
|
state.saved_interactive_session = nil
|
||||||
|
end
|
||||||
|
state.set_active_panel(nil)
|
||||||
|
logger.log('interactive closed')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if state.get_active_panel() then
|
||||||
|
logger.log('another panel is already active', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
state.saved_interactive_session = vim.fn.tempname()
|
||||||
|
vim.cmd(('mksession! %s'):format(state.saved_interactive_session))
|
||||||
|
vim.cmd('silent only')
|
||||||
|
|
||||||
|
local config = config_module.get_config()
|
||||||
|
local contest_config = config.contests[state.get_platform() or '']
|
||||||
|
local execute = require('cp.runner.execute')
|
||||||
|
local compile_result = execute.compile_problem(contest_config, false)
|
||||||
|
if not compile_result.success then
|
||||||
|
require('cp.runner.run').handle_compilation_failure(compile_result.output)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local binary = state.get_binary_file()
|
||||||
|
if not binary then
|
||||||
|
logger.log('no binary path found', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.cmd('terminal')
|
||||||
|
local term_buf = vim.api.nvim_get_current_buf()
|
||||||
|
local term_win = vim.api.nvim_get_current_win()
|
||||||
|
|
||||||
|
vim.fn.chansend(vim.b.terminal_job_id, binary .. '\n')
|
||||||
|
|
||||||
|
vim.keymap.set('t', config.run_panel.close_key, function()
|
||||||
|
M.toggle_interactive()
|
||||||
|
end, { buffer = term_buf, silent = true })
|
||||||
|
|
||||||
|
state.interactive_buf = term_buf
|
||||||
|
state.interactive_win = term_win
|
||||||
|
state.set_active_panel('interactive')
|
||||||
|
logger.log(('interactive opened, running %s'):format(binary))
|
||||||
|
end
|
||||||
|
|
||||||
function M.toggle_run_panel(is_debug)
|
function M.toggle_run_panel(is_debug)
|
||||||
if state.is_run_panel_active() then
|
if state.get_active_panel() == 'run' then
|
||||||
if current_diff_layout then
|
if current_diff_layout then
|
||||||
current_diff_layout.cleanup()
|
current_diff_layout.cleanup()
|
||||||
current_diff_layout = nil
|
current_diff_layout = nil
|
||||||
|
|
@ -21,12 +79,16 @@ function M.toggle_run_panel(is_debug)
|
||||||
vim.fn.delete(state.saved_session)
|
vim.fn.delete(state.saved_session)
|
||||||
state.saved_session = nil
|
state.saved_session = nil
|
||||||
end
|
end
|
||||||
|
state.set_active_panel(nil)
|
||||||
state.set_run_panel_active(false)
|
|
||||||
logger.log('test panel closed')
|
logger.log('test panel closed')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if state.get_active_panel() then
|
||||||
|
logger.log('another panel is already active', vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if not state.get_platform() then
|
if not state.get_platform() then
|
||||||
logger.log(
|
logger.log(
|
||||||
'No contest configured. Use :CP <platform> <contest> <problem> to set up first.',
|
'No contest configured. Use :CP <platform> <contest> <problem> to set up first.',
|
||||||
|
|
@ -55,13 +117,11 @@ function M.toggle_run_panel(is_debug)
|
||||||
if config.hooks and config.hooks.before_run then
|
if config.hooks and config.hooks.before_run then
|
||||||
config.hooks.before_run(state)
|
config.hooks.before_run(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_debug and config.hooks and config.hooks.before_debug then
|
if is_debug and config.hooks and config.hooks.before_debug then
|
||||||
config.hooks.before_debug(state)
|
config.hooks.before_debug(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
local run = require('cp.runner.run')
|
local run = require('cp.runner.run')
|
||||||
|
|
||||||
local input_file = state.get_input_file()
|
local input_file = state.get_input_file()
|
||||||
logger.log(('run panel: checking test cases for %s'):format(input_file or 'none'))
|
logger.log(('run panel: checking test cases for %s'):format(input_file or 'none'))
|
||||||
|
|
||||||
|
|
@ -72,7 +132,6 @@ function M.toggle_run_panel(is_debug)
|
||||||
|
|
||||||
state.saved_session = vim.fn.tempname()
|
state.saved_session = vim.fn.tempname()
|
||||||
vim.cmd(('mksession! %s'):format(state.saved_session))
|
vim.cmd(('mksession! %s'):format(state.saved_session))
|
||||||
|
|
||||||
vim.cmd('silent only')
|
vim.cmd('silent only')
|
||||||
|
|
||||||
local tab_buf = buffer_utils.create_buffer_with_options()
|
local tab_buf = buffer_utils.create_buffer_with_options()
|
||||||
|
|
@ -80,13 +139,8 @@ function M.toggle_run_panel(is_debug)
|
||||||
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
vim.api.nvim_win_set_buf(main_win, tab_buf)
|
||||||
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = tab_buf })
|
vim.api.nvim_set_option_value('filetype', 'cptest', { buf = tab_buf })
|
||||||
|
|
||||||
local test_windows = {
|
local test_windows = { tab_win = main_win }
|
||||||
tab_win = main_win,
|
local test_buffers = { tab_buf = tab_buf }
|
||||||
}
|
|
||||||
local test_buffers = {
|
|
||||||
tab_buf = tab_buf,
|
|
||||||
}
|
|
||||||
|
|
||||||
local test_list_namespace = vim.api.nvim_create_namespace('cp_test_list')
|
local test_list_namespace = vim.api.nvim_create_namespace('cp_test_list')
|
||||||
|
|
||||||
local setup_keybindings_for_buffer
|
local setup_keybindings_for_buffer
|
||||||
|
|
@ -106,10 +160,8 @@ function M.toggle_run_panel(is_debug)
|
||||||
if not test_buffers.tab_buf or not vim.api.nvim_buf_is_valid(test_buffers.tab_buf) then
|
if not test_buffers.tab_buf or not vim.api.nvim_buf_is_valid(test_buffers.tab_buf) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local run_render = require('cp.runner.run_render')
|
local run_render = require('cp.runner.run_render')
|
||||||
run_render.setup_highlights()
|
run_render.setup_highlights()
|
||||||
|
|
||||||
local test_state = run.get_run_panel_state()
|
local test_state = run.get_run_panel_state()
|
||||||
local tab_lines, tab_highlights = run_render.render_test_list(test_state)
|
local tab_lines, tab_highlights = run_render.render_test_list(test_state)
|
||||||
buffer_utils.update_buffer_content(
|
buffer_utils.update_buffer_content(
|
||||||
|
|
@ -118,7 +170,6 @@ function M.toggle_run_panel(is_debug)
|
||||||
tab_highlights,
|
tab_highlights,
|
||||||
test_list_namespace
|
test_list_namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
update_diff_panes()
|
update_diff_panes()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -127,14 +178,12 @@ function M.toggle_run_panel(is_debug)
|
||||||
if #test_state.test_cases == 0 then
|
if #test_state.test_cases == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
test_state.current_index = (test_state.current_index + delta) % #test_state.test_cases
|
test_state.current_index = (test_state.current_index + delta) % #test_state.test_cases
|
||||||
|
|
||||||
refresh_run_panel()
|
refresh_run_panel()
|
||||||
end
|
end
|
||||||
|
|
||||||
setup_keybindings_for_buffer = function(buf)
|
setup_keybindings_for_buffer = function(buf)
|
||||||
vim.keymap.set('n', 'q', function()
|
vim.keymap.set('n', config.run_panel.close_key, function()
|
||||||
M.toggle_run_panel()
|
M.toggle_run_panel()
|
||||||
end, { buffer = buf, silent = true })
|
end, { buffer = buf, silent = true })
|
||||||
vim.keymap.set('n', config.run_panel.toggle_diff_key, function()
|
vim.keymap.set('n', config.run_panel.toggle_diff_key, function()
|
||||||
|
|
@ -189,10 +238,9 @@ function M.toggle_run_panel(is_debug)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
vim.api.nvim_set_current_win(test_windows.tab_win)
|
vim.api.nvim_set_current_win(test_windows.tab_win)
|
||||||
|
|
||||||
state.set_run_panel_active(true)
|
|
||||||
state.test_buffers = test_buffers
|
state.test_buffers = test_buffers
|
||||||
state.test_windows = test_windows
|
state.test_windows = test_windows
|
||||||
|
state.set_active_panel('run')
|
||||||
local test_state = run.get_run_panel_state()
|
local test_state = run.get_run_panel_state()
|
||||||
logger.log(
|
logger.log(
|
||||||
string.format('test panel opened (%d test cases)', #test_state.test_cases),
|
string.format('test panel opened (%d test cases)', #test_state.test_cases),
|
||||||
|
|
|
||||||
|
|
@ -33,18 +33,15 @@ describe('cp command parsing', function()
|
||||||
get_problem_id = function()
|
get_problem_id = function()
|
||||||
return 'a'
|
return 'a'
|
||||||
end,
|
end,
|
||||||
is_run_panel_active = function()
|
|
||||||
return false
|
|
||||||
end,
|
|
||||||
set_platform = function() end,
|
set_platform = function() end,
|
||||||
set_contest_id = function() end,
|
set_contest_id = function() end,
|
||||||
set_problem_id = function() end,
|
set_problem_id = function() end,
|
||||||
set_run_panel_active = function() end,
|
|
||||||
}
|
}
|
||||||
package.loaded['cp.state'] = mock_state
|
package.loaded['cp.state'] = mock_state
|
||||||
|
|
||||||
local mock_ui_panel = {
|
local mock_ui_panel = {
|
||||||
toggle_run_panel = function() end,
|
toggle_run_panel = function() end,
|
||||||
|
toggle_interactive = function() end,
|
||||||
}
|
}
|
||||||
package.loaded['cp.ui.panel'] = mock_ui_panel
|
package.loaded['cp.ui.panel'] = mock_ui_panel
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue