feat(ansi): better logging and option to disab;e
This commit is contained in:
parent
bd81743274
commit
f60f6dd5bb
8 changed files with 159 additions and 10 deletions
|
|
@ -148,6 +148,7 @@ Here's an example configuration with lazy.nvim: >lua
|
|||
scrapers = { 'atcoder', 'codeforces', 'cses' },
|
||||
filename = default_filename, -- <contest id> + <problem id>
|
||||
run_panel = {
|
||||
ansi = true,
|
||||
diff_mode = 'vim',
|
||||
next_test_key = '<c-n>',
|
||||
prev_test_key = '<c-p>',
|
||||
|
|
@ -199,6 +200,12 @@ 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: "vim") Diff backend: "vim" or "git".
|
||||
Git provides character-level precision, vim uses
|
||||
built-in diff.
|
||||
|
|
|
|||
|
|
@ -192,6 +192,24 @@ function M.setup_highlight_groups()
|
|||
BrightWhite = vim.g.terminal_color_15,
|
||||
}
|
||||
|
||||
local missing_colors = {}
|
||||
for color_name, terminal_color in pairs(color_map) do
|
||||
if terminal_color == nil then
|
||||
table.insert(missing_colors, color_name)
|
||||
end
|
||||
end
|
||||
|
||||
if #missing_colors > 0 then
|
||||
vim.notify(
|
||||
string.format(
|
||||
'[cp.nvim] Terminal colors not configured: %s. ANSI colors will not display properly. '
|
||||
.. 'Set vim.g.terminal_color_* variables or use a colorscheme that provides them.',
|
||||
table.concat(missing_colors, ', ')
|
||||
),
|
||||
vim.log.levels.WARN
|
||||
)
|
||||
end
|
||||
|
||||
local combinations = {
|
||||
{ bold = false, italic = false },
|
||||
{ bold = true, italic = false },
|
||||
|
|
@ -202,7 +220,7 @@ function M.setup_highlight_groups()
|
|||
for _, combo in ipairs(combinations) do
|
||||
for color_name, terminal_color in pairs(color_map) do
|
||||
local parts = { 'CpAnsi' }
|
||||
local opts = { fg = terminal_color }
|
||||
local opts = { fg = terminal_color or 'NONE' }
|
||||
|
||||
if combo.bold then
|
||||
table.insert(parts, 'Bold')
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
---@field setup_code? fun(ctx: ProblemContext)
|
||||
|
||||
---@class RunPanelConfig
|
||||
---@field ansi boolean Enable ANSI color parsing and highlighting
|
||||
---@field diff_mode "vim"|"git" Diff backend to use
|
||||
---@field next_test_key string Key to navigate to next test case
|
||||
---@field prev_test_key string Key to navigate to previous test case
|
||||
|
|
@ -97,6 +98,7 @@ M.defaults = {
|
|||
scrapers = constants.PLATFORMS,
|
||||
filename = nil,
|
||||
run_panel = {
|
||||
ansi = true,
|
||||
diff_mode = 'vim',
|
||||
next_test_key = '<c-n>',
|
||||
prev_test_key = '<c-p>',
|
||||
|
|
@ -186,6 +188,11 @@ function M.setup(user_config)
|
|||
})
|
||||
|
||||
vim.validate({
|
||||
ansi = {
|
||||
config.run_panel.ansi,
|
||||
'boolean',
|
||||
'ansi color parsing must be enabled xor disabled',
|
||||
},
|
||||
diff_mode = {
|
||||
config.run_panel.diff_mode,
|
||||
function(value)
|
||||
|
|
|
|||
|
|
@ -537,8 +537,10 @@ local function toggle_run_panel(is_debug)
|
|||
refresh_run_panel()
|
||||
|
||||
vim.schedule(function()
|
||||
local ansi = require('cp.ansi')
|
||||
ansi.setup_highlight_groups()
|
||||
if config.run_panel.ansi then
|
||||
local ansi = require('cp.ansi')
|
||||
ansi.setup_highlight_groups()
|
||||
end
|
||||
if current_diff_layout then
|
||||
update_diff_panes()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -241,9 +241,13 @@ local function run_single_test_case(ctx, contest_config, cp_config, test_case)
|
|||
local actual_highlights = {}
|
||||
|
||||
if actual_output ~= '' then
|
||||
local parsed = ansi.parse_ansi_text(actual_output)
|
||||
actual_output = table.concat(parsed.lines, '\n')
|
||||
actual_highlights = parsed.highlights
|
||||
if cp_config.run_panel.ansi then
|
||||
local parsed = ansi.parse_ansi_text(actual_output)
|
||||
actual_output = table.concat(parsed.lines, '\n')
|
||||
actual_highlights = parsed.highlights
|
||||
else
|
||||
actual_output = actual_output:gsub('\027%[[%d;]*[a-zA-Z]', '')
|
||||
end
|
||||
end
|
||||
|
||||
local max_lines = cp_config.run_panel.max_output_lines
|
||||
|
|
@ -362,11 +366,18 @@ end
|
|||
|
||||
function M.handle_compilation_failure(compilation_output)
|
||||
local ansi = require('cp.ansi')
|
||||
local config = require('cp.config').setup()
|
||||
|
||||
-- Always parse the compilation output - it contains everything now
|
||||
local parsed = ansi.parse_ansi_text(compilation_output or '')
|
||||
local clean_text = table.concat(parsed.lines, '\n')
|
||||
local highlights = parsed.highlights
|
||||
local clean_text
|
||||
local highlights = {}
|
||||
|
||||
if config.run_panel.ansi then
|
||||
local parsed = ansi.parse_ansi_text(compilation_output or '')
|
||||
clean_text = table.concat(parsed.lines, '\n')
|
||||
highlights = parsed.highlights
|
||||
else
|
||||
clean_text = (compilation_output or ''):gsub('\027%[[%d;]*[a-zA-Z]', '')
|
||||
end
|
||||
|
||||
for _, test_case in ipairs(run_panel_state.test_cases) do
|
||||
test_case.status = 'fail'
|
||||
|
|
|
|||
|
|
@ -212,4 +212,32 @@ describe('ansi parser', function()
|
|||
assert.is_nil(state.foreground)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('setup_highlight_groups', function()
|
||||
it('creates highlight groups with fallback colors when terminal colors are nil', function()
|
||||
local original_colors = {}
|
||||
for i = 0, 15 do
|
||||
original_colors[i] = vim.g['terminal_color_' .. i]
|
||||
vim.g['terminal_color_' .. i] = nil
|
||||
end
|
||||
|
||||
ansi.setup_highlight_groups()
|
||||
|
||||
local highlight = vim.api.nvim_get_hl(0, { name = 'CpAnsiRed' })
|
||||
assert.equals('NONE', highlight.fg)
|
||||
|
||||
for i = 0, 15 do
|
||||
vim.g['terminal_color_' .. i] = original_colors[i]
|
||||
end
|
||||
end)
|
||||
|
||||
it('creates highlight groups with proper colors when terminal colors are set', function()
|
||||
vim.g.terminal_color_1 = '#ff0000'
|
||||
|
||||
ansi.setup_highlight_groups()
|
||||
|
||||
local highlight = vim.api.nvim_get_hl(0, { name = 'CpAnsiRed' })
|
||||
assert.equals('#ff0000', highlight.fg)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,16 @@ describe('cp.config', function()
|
|||
end)
|
||||
|
||||
describe('run_panel config validation', function()
|
||||
it('validates ansi is boolean', function()
|
||||
local invalid_config = {
|
||||
run_panel = { ansi = 'invalid' },
|
||||
}
|
||||
|
||||
assert.has_error(function()
|
||||
config.setup(invalid_config)
|
||||
end, 'ansi color parsing must be enabled xor disabled')
|
||||
end)
|
||||
|
||||
it('validates diff_mode values', function()
|
||||
local invalid_config = {
|
||||
run_panel = { diff_mode = 'invalid' },
|
||||
|
|
@ -114,6 +124,7 @@ describe('cp.config', function()
|
|||
it('accepts valid run_panel config', function()
|
||||
local valid_config = {
|
||||
run_panel = {
|
||||
ansi = false,
|
||||
diff_mode = 'git',
|
||||
next_test_key = 'j',
|
||||
prev_test_key = 'k',
|
||||
|
|
|
|||
65
spec/run_spec.lua
Normal file
65
spec/run_spec.lua
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
describe('run module ANSI processing', function()
|
||||
local run = require('cp.run')
|
||||
local config = require('cp.config')
|
||||
|
||||
describe('ANSI processing modes', function()
|
||||
it('parses ANSI when ansi config is true', function()
|
||||
local test_config = config.setup({ run_panel = { ansi = true } })
|
||||
|
||||
local result = {
|
||||
stdout = 'Hello \027[31mworld\027[0m!',
|
||||
stderr = '',
|
||||
code = 0,
|
||||
ok = true,
|
||||
signal = nil,
|
||||
timed_out = false,
|
||||
}
|
||||
|
||||
local processed = run.process_test_result(result, 'expected_output', test_config)
|
||||
|
||||
assert.equals('Hello world!', processed.actual)
|
||||
assert.equals(1, #processed.actual_highlights)
|
||||
assert.equals('CpAnsiRed', processed.actual_highlights[1].highlight_group)
|
||||
end)
|
||||
|
||||
it('strips ANSI when ansi config is false', function()
|
||||
local test_config = config.setup({ run_panel = { ansi = false } })
|
||||
|
||||
local result = {
|
||||
stdout = 'Hello \027[31mworld\027[0m!',
|
||||
stderr = '',
|
||||
code = 0,
|
||||
ok = true,
|
||||
signal = nil,
|
||||
timed_out = false,
|
||||
}
|
||||
|
||||
local processed = run.process_test_result(result, 'expected_output', test_config)
|
||||
|
||||
assert.equals('Hello world!', processed.actual)
|
||||
assert.equals(0, #processed.actual_highlights)
|
||||
end)
|
||||
|
||||
it('handles compilation failure with ansi disabled', function()
|
||||
local test_config = config.setup({ run_panel = { ansi = false } })
|
||||
|
||||
local compilation_output = 'error.cpp:1:1: \027[1m\027[31merror:\027[0m undefined variable'
|
||||
|
||||
-- Create mock run panel state
|
||||
run._test_set_panel_state({
|
||||
test_cases = {
|
||||
{ index = 1, input = 'test', expected = 'expected' },
|
||||
},
|
||||
})
|
||||
|
||||
run.handle_compilation_failure(compilation_output)
|
||||
|
||||
local panel_state = run.get_run_panel_state()
|
||||
local test_case = panel_state.test_cases[1]
|
||||
|
||||
assert.equals('error.cpp:1:1: error: undefined variable', test_case.actual)
|
||||
assert.equals(0, #test_case.actual_highlights)
|
||||
assert.equals('Compilation failed', test_case.error)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue