feat: interactive terminal

This commit is contained in:
Barrett Ruth 2025-09-26 08:28:19 -04:00
parent 6b4dd32683
commit 7efd6404b6
6 changed files with 82 additions and 23 deletions

View file

@ -152,7 +152,7 @@ Here's an example configuration with lazy.nvim: >lua
diff_mode = 'vim',
next_test_key = '<c-n>',
prev_test_key = '<c-p>',
toggle_diff_key = 't',
toggle_diff_key = '<c-q>',
max_output_lines = 50,
},
diff = {
@ -203,18 +203,19 @@ Here's an example configuration with lazy.nvim: >lua
*cp.RunPanelConfig*
Fields: ~
{ansi} (boolean, default: true) Enable ANSI color parsing and
highlighting. When true, compiler output and test results
display with colored syntax highlighting. When false,
ANSI escape codes are stripped for plain text display.
Requires vim.g.terminal_color_* to be configured for
proper color display.
{diff_mode} (string, default: "none") Diff backend: "none", "vim", or "git".
"none" displays plain buffers without highlighting,
"vim" uses built-in diff, "git" provides character-level precision.
{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.
{toggle_diff_key} (string, default: "t") Key to cycle through diff modes.
{ansi} (boolean, default: true) Enable ANSI color parsing and
highlighting. When true, compiler output and test results
display with colored syntax highlighting. When false,
ANSI escape codes are stripped for plain text display.
Requires vim.g.terminal_color_* to be configured for
proper color display.
{diff_mode} (string, default: "none") Diff backend: "none", "vim", or "git".
"none" displays plain buffers without highlighting,
"vim" uses built-in diff, "git" provides character-level precision.
{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.
{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.
*cp.DiffConfig*
@ -531,9 +532,9 @@ RUN PANEL KEYMAPS *cp-test-keys*
run_panel.next_test_key)
<c-p> Navigate to previous test case (configurable via
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)
q Exit test panel and restore layout
<c-q> Exit run panel/interactive terminal and restore layout
Diff Modes ~

View file

@ -126,8 +126,10 @@ function M.handle_command(opts)
local setup = require('cp.setup')
local ui = require('cp.ui.panel')
if cmd.action == 'run' then
ui.toggle_run_panel(cmd.debug)
if cmd.action == 'interact' then
ui.toggle_interactive()
elseif cmd.action == 'run' then
ui.close_run_panel(cmd.debug)
elseif cmd.action == 'next' then
setup.navigate_problem(1, cmd.language)
elseif cmd.action == 'prev' then

View file

@ -34,6 +34,7 @@
---@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 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
---@class DiffGitConfig
@ -103,7 +104,8 @@ M.defaults = {
diff_mode = 'none',
next_test_key = '<c-n>',
prev_test_key = '<c-p>',
toggle_diff_key = 't',
toggle_diff_key = '<c-t>',
close_key = '<c-q>',
max_output_lines = 50,
},
diff = {
@ -229,6 +231,13 @@ function M.setup(user_config)
end,
'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 = {
config.run_panel.max_output_lines,
function(value)

View file

@ -1,7 +1,7 @@
local M = {}
M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' }
M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache' }
M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache', 'interact' }
M.PLATFORM_DISPLAY_NAMES = {
atcoder = 'AtCoder',

View file

@ -9,7 +9,54 @@ local state = require('cp.state')
local current_diff_layout = nil
local current_mode = nil
function M.toggle_run_panel(is_debug)
function M.toggle_interactive()
if state.is_interactive_active then
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.is_interactive_active = false
logger.log('interactive closed')
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', '<c-q>', function()
M.toggle_interactive()
end, { buffer = term_buf, silent = true })
state.is_interactive_active = true
state.interactive_buf = term_buf
state.interactive_win = term_win
logger.log(('interactive opened, running %s'):format(binary))
end
function M.close_run_panel(is_debug)
if state.is_run_panel_active() then
if current_diff_layout then
current_diff_layout.cleanup()
@ -134,8 +181,8 @@ function M.toggle_run_panel(is_debug)
end
setup_keybindings_for_buffer = function(buf)
vim.keymap.set('n', 'q', function()
M.toggle_run_panel()
vim.keymap.set('n', config.run_panel.close_key, function()
M.close_run_panel()
end, { buffer = buf, silent = true })
vim.keymap.set('n', config.run_panel.toggle_diff_key, function()
local modes = { 'none', 'git', 'vim' }

View file

@ -44,7 +44,7 @@ describe('cp command parsing', function()
package.loaded['cp.state'] = mock_state
local mock_ui_panel = {
toggle_run_panel = function() end,
close_run_panel = function() end,
}
package.loaded['cp.ui.panel'] = mock_ui_panel