feat(test: git diff backend
This commit is contained in:
parent
289e6efe62
commit
d193fabfb9
3 changed files with 110 additions and 67 deletions
87
doc/cp.txt
87
doc/cp.txt
|
|
@ -50,8 +50,8 @@ Setup Commands ~
|
|||
Action Commands ~
|
||||
|
||||
:CP test [--debug] Toggle test panel for individual test case
|
||||
debugging. Shows per-test results with three-pane
|
||||
layout for easy Expected/Actual comparison.
|
||||
debugging. Shows per-test results with redesigned
|
||||
layout for efficient comparison.
|
||||
Use --debug flag to compile with debug flags
|
||||
Requires contest setup first.
|
||||
|
||||
|
|
@ -115,6 +115,21 @@ Optional configuration with lazy.nvim: >
|
|||
vim.diagnostic.enable(false)
|
||||
end,
|
||||
},
|
||||
test_panel = {
|
||||
diff_mode = "vim", -- "vim" or "git"
|
||||
toggle_key = "t", -- key to toggle test panel
|
||||
status_format = "compact", -- "compact" or "verbose"
|
||||
},
|
||||
diff = {
|
||||
git = {
|
||||
command = "git",
|
||||
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,
|
||||
}
|
||||
|
|
@ -131,6 +146,8 @@ Optional configuration with lazy.nvim: >
|
|||
during operation.
|
||||
• {scrapers} (`table<string,boolean>`) Per-platform scraper control.
|
||||
Default enables all platforms.
|
||||
• {test_panel} (`TestPanelConfig`) Test panel behavior configuration.
|
||||
• {diff} (`DiffConfig`) Diff backend configuration.
|
||||
• {filename}? (`function`) Custom filename generation function.
|
||||
`function(contest, contest_id, problem_id, config, language)`
|
||||
Should return full filename with extension.
|
||||
|
|
@ -157,6 +174,20 @@ Optional configuration with lazy.nvim: >
|
|||
• {extension} (`string`) File extension (e.g. "cc", "py").
|
||||
• {executable}? (`string`) Executable name for interpreted languages.
|
||||
|
||||
*cp.TestPanelConfig*
|
||||
|
||||
Fields: ~
|
||||
• {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.
|
||||
|
||||
*cp.DiffConfig*
|
||||
|
||||
Fields: ~
|
||||
• {git} (`DiffGitConfig`) Git diff backend configuration.
|
||||
• {vim} (`DiffVimConfig`) Vim diff backend configuration.
|
||||
|
||||
*cp.Hooks*
|
||||
|
||||
Fields: ~
|
||||
|
|
@ -256,8 +287,9 @@ Example: Quick setup for single Codeforces problem >
|
|||
|
||||
TEST PANEL *cp-test*
|
||||
|
||||
The test panel provides individual test case debugging with a three-pane
|
||||
layout showing test list, expected output, and actual output side-by-side.
|
||||
The test panel provides individual test case debugging with a streamlined
|
||||
layout optimized for modern screens. Shows test status with competitive
|
||||
programming terminology and efficient space usage.
|
||||
|
||||
Activation ~
|
||||
*:CP-test*
|
||||
|
|
@ -270,29 +302,46 @@ Activation ~
|
|||
|
||||
Interface ~
|
||||
|
||||
The test panel uses a three-pane layout for easy comparison: >
|
||||
The test panel uses a redesigned two-pane layout for efficient comparison:
|
||||
(note that the diff is indeed highlighted, not the weird amalgamation of
|
||||
characters below) >
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 1. [ok:true ] [code:0] [time:12ms] │
|
||||
│> 2. [ok:false] [code:0] [time:45ms] │
|
||||
│ │
|
||||
│ Input: │
|
||||
│ 5 3 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌─ Expected ──────────────────┐ ┌───── Actual ────────────────┐
|
||||
│ 8 │ │ 7 │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────────────────────────┘ └─────────────────────────────┘
|
||||
┌─ Tests ─────────────────────┐ ┌─ Expected vs Actual ───────────────────────┐
|
||||
│ AC 1. 12ms │ │ 45ms │ Exit: 0 │
|
||||
│ WA > 2. 45ms │ ├────────────────────────────────────────────┤
|
||||
│ 5 3 │ │ │
|
||||
│ │ │ 4[-2-]{+3+} │
|
||||
│ AC 3. 9ms │ │ 100 │
|
||||
│ RTE 4. 0ms │ │ hello w[-o-]r{+o+}ld │
|
||||
│ │ │ │
|
||||
└─────────────────────────────┘ └────────────────────────────────────────────┘
|
||||
<
|
||||
|
||||
Status Indicators ~
|
||||
|
||||
Test cases use competitive programming terminology:
|
||||
|
||||
AC Accepted (passed)
|
||||
WA Wrong Answer (output mismatch)
|
||||
TLE Time Limit Exceeded (timeout)
|
||||
RTE Runtime Error (non-zero exit)
|
||||
|
||||
Keymaps ~
|
||||
*cp-test-keys*
|
||||
<c-n> Navigate to next test case
|
||||
<c-p> Navigate to previous test case
|
||||
q Exit test panel (restore layout)
|
||||
t Toggle test panel (configurable via test_panel.toggle_key)
|
||||
|
||||
Diff Modes ~
|
||||
|
||||
Two diff backends are available:
|
||||
|
||||
vim Built-in vim diff (default, always available)
|
||||
git Character-level git word-diff (requires git, more precise)
|
||||
|
||||
The git backend shows character-level changes with [-removed-] and {+added+}
|
||||
markers for precise difference analysis.
|
||||
|
||||
Execution Details ~
|
||||
|
||||
|
|
|
|||
|
|
@ -48,42 +48,16 @@ local git_backend = {
|
|||
vim.fn.delete(tmp_actual)
|
||||
|
||||
if result.code == 0 then
|
||||
-- No differences, return actual content as-is
|
||||
return {
|
||||
content = vim.split(actual, '\n', { plain = true, trimempty = true }),
|
||||
highlights = {}
|
||||
}
|
||||
else
|
||||
-- Parse git diff output
|
||||
local lines = vim.split(result.stdout or '', '\n', { plain = true })
|
||||
local content_lines = {}
|
||||
local highlights = {}
|
||||
|
||||
-- Skip git diff header lines (start with @@, +++, ---, etc.)
|
||||
local content_started = false
|
||||
for _, line in ipairs(lines) do
|
||||
if content_started or (not line:match('^@@') and not line:match('^%+%+%+') and not line:match('^%-%-%-') and not line:match('^index')) then
|
||||
content_started = true
|
||||
if line:match('^[^%-+]') or line:match('^%+') then
|
||||
-- Skip lines starting with - (removed lines) for the actual pane
|
||||
-- Only show lines that are unchanged or added
|
||||
if not line:match('^%-') then
|
||||
local clean_line = line:gsub('^%+', '') -- Remove + prefix
|
||||
table.insert(content_lines, clean_line)
|
||||
|
||||
-- Parse highlights will be handled in highlight.lua
|
||||
table.insert(highlights, {
|
||||
line = #content_lines,
|
||||
content = clean_line
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local highlight_module = require('cp.highlight')
|
||||
return {
|
||||
content = content_lines,
|
||||
highlights = highlights
|
||||
content = {},
|
||||
highlights = {},
|
||||
raw_diff = result.stdout or ''
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -122,18 +96,12 @@ end
|
|||
function M.get_best_backend(preferred_backend)
|
||||
if preferred_backend and backends[preferred_backend] then
|
||||
if preferred_backend == 'git' and not M.is_git_available() then
|
||||
-- Fall back to vim if git is not available
|
||||
return backends.vim
|
||||
end
|
||||
return backends[preferred_backend]
|
||||
end
|
||||
|
||||
-- Default to git if available, otherwise vim
|
||||
if M.is_git_available() then
|
||||
return backends.git
|
||||
else
|
||||
return backends.vim
|
||||
end
|
||||
return backends.vim
|
||||
end
|
||||
|
||||
---Render diff using specified backend
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@ local function toggle_test_panel(is_debug)
|
|||
actual_buf = actual_buf,
|
||||
}
|
||||
|
||||
local highlight = require('cp.highlight')
|
||||
local diff_namespace = highlight.create_namespace()
|
||||
|
||||
local function render_test_tabs()
|
||||
local test_render = require('cp.test_render')
|
||||
test_render.setup_highlights()
|
||||
|
|
@ -250,6 +253,15 @@ local function toggle_test_panel(is_debug)
|
|||
local expected_lines = vim.split(expected_text, '\n', { plain = true, trimempty = true })
|
||||
|
||||
update_buffer_content(test_buffers.expected_buf, expected_lines)
|
||||
|
||||
local diff_backend = require('cp.diff')
|
||||
local backend = diff_backend.get_best_backend(config.test_panel.diff_mode)
|
||||
|
||||
if backend.name == 'vim' and current_test.status == 'fail' then
|
||||
vim.api.nvim_set_option_value('diff', true, { win = test_windows.expected_win })
|
||||
else
|
||||
vim.api.nvim_set_option_value('diff', false, { win = test_windows.expected_win })
|
||||
end
|
||||
end
|
||||
|
||||
local function update_actual_pane()
|
||||
|
|
@ -270,24 +282,38 @@ local function toggle_test_panel(is_debug)
|
|||
actual_lines = { '(not run yet)' }
|
||||
end
|
||||
|
||||
update_buffer_content(test_buffers.actual_buf, actual_lines)
|
||||
|
||||
local test_render = require('cp.test_render')
|
||||
local status_bar_text = test_render.render_status_bar(current_test)
|
||||
if status_bar_text ~= '' then
|
||||
vim.api.nvim_set_option_value('winbar', status_bar_text, { win = test_windows.actual_win })
|
||||
end
|
||||
|
||||
vim.api.nvim_set_option_value('diff', enable_diff, { win = test_windows.expected_win })
|
||||
vim.api.nvim_set_option_value('diff', enable_diff, { win = test_windows.actual_win })
|
||||
|
||||
if enable_diff then
|
||||
vim.api.nvim_win_call(test_windows.expected_win, function()
|
||||
vim.cmd.diffthis()
|
||||
end)
|
||||
vim.api.nvim_win_call(test_windows.actual_win, function()
|
||||
vim.cmd.diffthis()
|
||||
end)
|
||||
local diff_backend = require('cp.diff')
|
||||
local backend = diff_backend.get_best_backend(config.test_panel.diff_mode)
|
||||
|
||||
if backend.name == 'git' then
|
||||
local diff_result = backend.render(current_test.expected, current_test.actual)
|
||||
if diff_result.raw_diff and diff_result.raw_diff ~= '' then
|
||||
highlight.parse_and_apply_diff(test_buffers.actual_buf, diff_result.raw_diff, diff_namespace)
|
||||
else
|
||||
update_buffer_content(test_buffers.actual_buf, actual_lines)
|
||||
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()
|
||||
end)
|
||||
vim.api.nvim_win_call(test_windows.actual_win, function()
|
||||
vim.cmd.diffthis()
|
||||
end)
|
||||
end
|
||||
else
|
||||
update_buffer_content(test_buffers.actual_buf, actual_lines)
|
||||
vim.api.nvim_set_option_value('diff', false, { win = test_windows.expected_win })
|
||||
vim.api.nvim_set_option_value('diff', false, { win = test_windows.actual_win })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue