diff --git a/doc/cp.txt b/doc/cp.txt index 4e8f3ac..1404d65 100644 --- a/doc/cp.txt +++ b/doc/cp.txt @@ -85,7 +85,7 @@ Optional configuration with lazy.nvim: > 'g++', '-std=c++{version}', '-O2', '-Wall', '-Wextra', '-DLOCAL', '{source}', '-o', '{binary}', }, - run = { '{binary}' }, + test = { '{binary}' }, debug = { 'g++', '-std=c++{version}', '-g3', '-fsanitize=address,undefined', '-DLOCAL', @@ -95,7 +95,7 @@ Optional configuration with lazy.nvim: > extension = "cc", }, python = { - run = { 'python3', '{source}' }, + test = { 'python3', '{source}' }, debug = { 'python3', '{source}' }, extension = "py", }, @@ -104,11 +104,8 @@ Optional configuration with lazy.nvim: > }, }, hooks = { - before_run = function(ctx) vim.cmd.w() end, - before_debug = function(ctx) - -- ctx.problem_id, ctx.platform, ctx.source_file, etc. - vim.cmd.w() - end, + before_test = function(ctx) vim.cmd.w() end, + before_debug = function(ctx) ... end, setup_code = function(ctx) vim.wo.foldmethod = "marker" vim.wo.foldmarker = "{{{,}}}" @@ -116,9 +113,10 @@ Optional configuration with lazy.nvim: > end, }, test_panel = { - diff_mode = "vim", -- "vim" or "git" - toggle_key = "t", -- key to toggle test panel - status_format = "compact", -- "compact" or "verbose" + diff_mode = "vim", -- "vim" or "git" + toggle_key = "t", -- toggle test panel + next_test_key = "", -- navigate to next test case + prev_test_key = "", -- navigate to previous test case }, diff = { git = { @@ -126,9 +124,6 @@ Optional configuration with lazy.nvim: > args = {"diff", "--no-index", "--word-diff=plain", "--word-diff-regex=.", "--no-prefix"}, }, - vim = { - enable_diffthis = true, - }, }, snippets = { ... }, -- LuaSnip snippets filename = function(contest, contest_id, problem_id, config, language) ... end, @@ -168,7 +163,7 @@ Optional configuration with lazy.nvim: > Fields: ~ • {compile}? (`string[]`) Compile command template with `{version}`, `{source}`, `{binary}` placeholders. - • {run} (`string[]`) Run command template. + • {test} (`string[]`) Test execution command template. • {debug}? (`string[]`) Debug compile command template. • {version}? (`number`) Language version (e.g. 20, 23 for C++). • {extension} (`string`) File extension (e.g. "cc", "py"). @@ -180,23 +175,38 @@ Optional configuration with lazy.nvim: > • {diff_mode} (`string`, default: `"vim"`) Diff backend: "vim" or "git". Git provides character-level precision, vim uses built-in diff. • {toggle_key} (`string`, default: `"t"`) Key to toggle test panel. - • {status_format} (`string`, default: `"compact"`) Status display format. + • {next_test_key} (`string`, default: `""`) Key to navigate to next test case. + • {prev_test_key} (`string`, default: `""`) Key to navigate to previous test case. *cp.DiffConfig* Fields: ~ • {git} (`DiffGitConfig`) Git diff backend configuration. - • {vim} (`DiffVimConfig`) Vim diff backend configuration. *cp.Hooks* Fields: ~ + • {before_test}? (`function`) Called before test panel opens. + `function(ctx: ProblemContext)` • {before_debug}? (`function`) Called before debug compilation. `function(ctx: ProblemContext)` • {setup_code}? (`function`) Called after source file is opened. - Used to configure buffer settings. + Good for configuring buffer settings. `function(ctx: ProblemContext)` +*ProblemContext* + + Fields: ~ + • {contest} (`string`) Platform name (e.g. "atcoder", "codeforces") + • {contest_id} (`string`) Contest ID (e.g. "abc123", "1933") + • {problem_id}? (`string`) Problem ID (e.g. "a", "b") - nil for CSES + • {source_file} (`string`) Source filename (e.g. "abc123a.cpp") + • {binary_file} (`string`) Binary output path (e.g. "build/abc123a.run") + • {input_file} (`string`) Test input path (e.g. "io/abc123a.cpin") + • {output_file} (`string`) Program output path (e.g. "io/abc123a.cpout") + • {expected_file} (`string`) Expected output path (e.g. "io/abc123a.expected") + • {problem_name} (`string`) Display name (e.g. "abc123a") + WORKFLOW *cp-workflow* For the sake of consistency and simplicity, cp.nvim extracts contest/problem identifiers from @@ -328,8 +338,8 @@ Test cases use competitive programming terminology: Keymaps ~ *cp-test-keys* - Navigate to next test case - Navigate to previous test case + Navigate to next test case (configurable via test_panel.next_test_key) + Navigate to previous test case (configurable via test_panel.prev_test_key) q Exit test panel (restore layout) t Toggle test panel (configurable via test_panel.toggle_key) diff --git a/lua/cp/config.lua b/lua/cp/config.lua index ca82b99..6c67c95 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -1,6 +1,6 @@ ---@class LanguageConfig ---@field compile? string[] Compile command template ----@field run string[] Run command template +---@field test string[] Test execution command template ---@field debug? string[] Debug command template ---@field executable? string Executable name ---@field version? number Language version @@ -8,7 +8,7 @@ ---@class PartialLanguageConfig ---@field compile? string[] Compile command template ----@field run? string[] Run command template +---@field test? string[] Test execution command template ---@field debug? string[] Debug command template ---@field executable? string Executable name ---@field version? number Language version @@ -27,25 +27,22 @@ ---@field timeout_ms? number ---@class Hooks ----@field before_run? fun(ctx: ProblemContext) +---@field before_test? fun(ctx: ProblemContext) ---@field before_debug? fun(ctx: ProblemContext) ---@field setup_code? fun(ctx: ProblemContext) ---@class TestPanelConfig ---@field diff_mode "vim"|"git" Diff backend to use ---@field toggle_key string Key to toggle test panel ----@field status_format "compact"|"verbose" Status display format +---@field next_test_key string Key to navigate to next test case +---@field prev_test_key string Key to navigate to previous test case ---@class DiffGitConfig ---@field command string Git executable name ---@field args string[] Additional git diff arguments ----@class DiffVimConfig ----@field enable_diffthis boolean Enable vim's diffthis - ---@class DiffConfig ---@field git DiffGitConfig ----@field vim DiffVimConfig ---@class cp.Config ---@field contests table @@ -75,7 +72,7 @@ M.defaults = { contests = {}, snippets = {}, hooks = { - before_run = nil, + before_test = nil, before_debug = nil, setup_code = nil, }, @@ -85,16 +82,14 @@ M.defaults = { test_panel = { diff_mode = 'vim', toggle_key = 't', - status_format = 'compact', + next_test_key = '', + prev_test_key = '', }, diff = { git = { command = 'git', args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' }, }, - vim = { - enable_diffthis = true, - }, }, } @@ -119,8 +114,8 @@ function M.setup(user_config) if user_config.hooks then vim.validate({ - before_run = { - user_config.hooks.before_run, + before_test = { + user_config.hooks.before_test, { 'function', 'nil' }, true, }, @@ -146,13 +141,26 @@ function M.setup(user_config) end, "diff_mode must be 'vim' or 'git'", }, - toggle_key = { user_config.test_panel.toggle_key, 'string', true }, - status_format = { - user_config.test_panel.status_format, + toggle_key = { + user_config.test_panel.toggle_key, function(value) - return vim.tbl_contains({ 'compact', 'verbose' }, value) + return type(value) == 'string' and value ~= '' end, - "status_format must be 'compact' or 'verbose'", + 'toggle_key must be a non-empty string', + }, + next_test_key = { + user_config.test_panel.next_test_key, + function(value) + return type(value) == 'string' and value ~= '' + end, + 'next_test_key must be a non-empty string', + }, + prev_test_key = { + user_config.test_panel.prev_test_key, + function(value) + return type(value) == 'string' and value ~= '' + end, + 'prev_test_key must be a non-empty string', }, }) end @@ -160,7 +168,6 @@ function M.setup(user_config) if user_config.diff then vim.validate({ git = { user_config.diff.git, { 'table', 'nil' }, true }, - vim = { user_config.diff.vim, { 'table', 'nil' }, true }, }) end diff --git a/lua/cp/execute.lua b/lua/cp/execute.lua index 1d25b87..3c7a2bf 100644 --- a/lua/cp/execute.lua +++ b/lua/cp/execute.lua @@ -278,7 +278,7 @@ function M.run_problem(ctx, contest_config, is_debug) input_data = table.concat(vim.fn.readfile(ctx.input_file), '\n') .. '\n' end - local run_cmd = build_command(language_config.run, language_config.executable, substitutions) + local run_cmd = build_command(language_config.test, language_config.executable, substitutions) local exec_result = execute_command(run_cmd, input_data, contest_config.timeout_ms) local formatted_output = format_output(exec_result, ctx.expected_file, is_debug) diff --git a/lua/cp/init.lua b/lua/cp/init.lua index f0e4e61..0792b37 100644 --- a/lua/cp/init.lua +++ b/lua/cp/init.lua @@ -232,7 +232,7 @@ local function toggle_test_panel(is_debug) local test_render = require('cp.test_render') test_render.setup_highlights() local test_state = test_module.get_test_panel_state() - return test_render.render_test_list(test_state, config.test_panel) + return test_render.render_test_list(test_state) end local function update_buffer_content(bufnr, lines) @@ -305,7 +305,6 @@ local function toggle_test_panel(is_debug) end else update_buffer_content(test_buffers.actual_buf, actual_lines) - vim.api.nvim_set_option_value('diff', true, { win = test_windows.expected_win }) vim.api.nvim_set_option_value('diff', true, { win = test_windows.actual_win }) vim.api.nvim_win_call(test_windows.expected_win, function() vim.cmd.diffthis() @@ -349,10 +348,10 @@ local function toggle_test_panel(is_debug) refresh_test_panel() end - vim.keymap.set('n', '', function() + vim.keymap.set('n', config.test_panel.next_test_key, function() navigate_test_case(1) end, { buffer = test_buffers.tab_buf, silent = true }) - vim.keymap.set('n', '', function() + vim.keymap.set('n', config.test_panel.prev_test_key, function() navigate_test_case(-1) end, { buffer = test_buffers.tab_buf, silent = true }) @@ -365,6 +364,10 @@ local function toggle_test_panel(is_debug) end, { buffer = buf, silent = true }) end + if config.hooks and config.hooks.before_test then + config.hooks.before_test(ctx) + end + if is_debug and config.hooks and config.hooks.before_debug then config.hooks.before_debug(ctx) end diff --git a/lua/cp/test.lua b/lua/cp/test.lua index a1fdd85..e4719e6 100644 --- a/lua/cp/test.lua +++ b/lua/cp/test.lua @@ -172,7 +172,7 @@ local function run_single_test_case(ctx, contest_config, test_case) end end - local run_cmd = build_command(language_config.run, language_config.executable, substitutions) + local run_cmd = build_command(language_config.test, language_config.executable, substitutions) local stdin_content = test_case.input .. '\n' diff --git a/lua/cp/test_render.lua b/lua/cp/test_render.lua index 904b445..9bf92c9 100644 --- a/lua/cp/test_render.lua +++ b/lua/cp/test_render.lua @@ -1,6 +1,3 @@ ----@class TestRenderConfig ----@field status_format "compact"|"verbose" - ---@class StatusInfo ---@field text string ---@field highlight_group string @@ -32,10 +29,8 @@ end ---Render test cases list with improved layout ---@param test_state TestPanelState ----@param config? TestRenderConfig ---@return string[] -function M.render_test_list(test_state, config) - config = config or { status_format = 'compact' } +function M.render_test_list(test_state) local lines = {} for i, test_case in ipairs(test_state.test_cases) do