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' },
|
scrapers = { 'atcoder', 'codeforces', 'cses' },
|
||||||
filename = default_filename, -- <contest id> + <problem id>
|
filename = default_filename, -- <contest id> + <problem id>
|
||||||
run_panel = {
|
run_panel = {
|
||||||
|
ansi = true,
|
||||||
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>',
|
||||||
|
|
@ -199,6 +200,12 @@ Here's an example configuration with lazy.nvim: >lua
|
||||||
|
|
||||||
*cp.RunPanelConfig*
|
*cp.RunPanelConfig*
|
||||||
Fields: ~
|
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".
|
{diff_mode} (string, default: "vim") Diff backend: "vim" or "git".
|
||||||
Git provides character-level precision, vim uses
|
Git provides character-level precision, vim uses
|
||||||
built-in diff.
|
built-in diff.
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,24 @@ function M.setup_highlight_groups()
|
||||||
BrightWhite = vim.g.terminal_color_15,
|
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 = {
|
local combinations = {
|
||||||
{ bold = false, italic = false },
|
{ bold = false, italic = false },
|
||||||
{ bold = true, italic = false },
|
{ bold = true, italic = false },
|
||||||
|
|
@ -202,7 +220,7 @@ function M.setup_highlight_groups()
|
||||||
for _, combo in ipairs(combinations) do
|
for _, combo in ipairs(combinations) do
|
||||||
for color_name, terminal_color in pairs(color_map) do
|
for color_name, terminal_color in pairs(color_map) do
|
||||||
local parts = { 'CpAnsi' }
|
local parts = { 'CpAnsi' }
|
||||||
local opts = { fg = terminal_color }
|
local opts = { fg = terminal_color or 'NONE' }
|
||||||
|
|
||||||
if combo.bold then
|
if combo.bold then
|
||||||
table.insert(parts, 'Bold')
|
table.insert(parts, 'Bold')
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
---@field setup_code? fun(ctx: ProblemContext)
|
---@field setup_code? fun(ctx: ProblemContext)
|
||||||
|
|
||||||
---@class RunPanelConfig
|
---@class RunPanelConfig
|
||||||
|
---@field ansi boolean Enable ANSI color parsing and highlighting
|
||||||
---@field diff_mode "vim"|"git" Diff backend to use
|
---@field diff_mode "vim"|"git" Diff backend to use
|
||||||
---@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
|
||||||
|
|
@ -97,6 +98,7 @@ M.defaults = {
|
||||||
scrapers = constants.PLATFORMS,
|
scrapers = constants.PLATFORMS,
|
||||||
filename = nil,
|
filename = nil,
|
||||||
run_panel = {
|
run_panel = {
|
||||||
|
ansi = true,
|
||||||
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>',
|
||||||
|
|
@ -186,6 +188,11 @@ function M.setup(user_config)
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.validate({
|
vim.validate({
|
||||||
|
ansi = {
|
||||||
|
config.run_panel.ansi,
|
||||||
|
'boolean',
|
||||||
|
'ansi color parsing must be enabled xor disabled',
|
||||||
|
},
|
||||||
diff_mode = {
|
diff_mode = {
|
||||||
config.run_panel.diff_mode,
|
config.run_panel.diff_mode,
|
||||||
function(value)
|
function(value)
|
||||||
|
|
|
||||||
|
|
@ -537,8 +537,10 @@ local function toggle_run_panel(is_debug)
|
||||||
refresh_run_panel()
|
refresh_run_panel()
|
||||||
|
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
local ansi = require('cp.ansi')
|
if config.run_panel.ansi then
|
||||||
ansi.setup_highlight_groups()
|
local ansi = require('cp.ansi')
|
||||||
|
ansi.setup_highlight_groups()
|
||||||
|
end
|
||||||
if current_diff_layout then
|
if current_diff_layout then
|
||||||
update_diff_panes()
|
update_diff_panes()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -241,9 +241,13 @@ local function run_single_test_case(ctx, contest_config, cp_config, test_case)
|
||||||
local actual_highlights = {}
|
local actual_highlights = {}
|
||||||
|
|
||||||
if actual_output ~= '' then
|
if actual_output ~= '' then
|
||||||
local parsed = ansi.parse_ansi_text(actual_output)
|
if cp_config.run_panel.ansi then
|
||||||
actual_output = table.concat(parsed.lines, '\n')
|
local parsed = ansi.parse_ansi_text(actual_output)
|
||||||
actual_highlights = parsed.highlights
|
actual_output = table.concat(parsed.lines, '\n')
|
||||||
|
actual_highlights = parsed.highlights
|
||||||
|
else
|
||||||
|
actual_output = actual_output:gsub('\027%[[%d;]*[a-zA-Z]', '')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local max_lines = cp_config.run_panel.max_output_lines
|
local max_lines = cp_config.run_panel.max_output_lines
|
||||||
|
|
@ -362,11 +366,18 @@ end
|
||||||
|
|
||||||
function M.handle_compilation_failure(compilation_output)
|
function M.handle_compilation_failure(compilation_output)
|
||||||
local ansi = require('cp.ansi')
|
local ansi = require('cp.ansi')
|
||||||
|
local config = require('cp.config').setup()
|
||||||
|
|
||||||
-- Always parse the compilation output - it contains everything now
|
local clean_text
|
||||||
local parsed = ansi.parse_ansi_text(compilation_output or '')
|
local highlights = {}
|
||||||
local clean_text = table.concat(parsed.lines, '\n')
|
|
||||||
local highlights = parsed.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
|
for _, test_case in ipairs(run_panel_state.test_cases) do
|
||||||
test_case.status = 'fail'
|
test_case.status = 'fail'
|
||||||
|
|
|
||||||
|
|
@ -212,4 +212,32 @@ describe('ansi parser', function()
|
||||||
assert.is_nil(state.foreground)
|
assert.is_nil(state.foreground)
|
||||||
end)
|
end)
|
||||||
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)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,16 @@ describe('cp.config', function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('run_panel config validation', function()
|
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()
|
it('validates diff_mode values', function()
|
||||||
local invalid_config = {
|
local invalid_config = {
|
||||||
run_panel = { diff_mode = 'invalid' },
|
run_panel = { diff_mode = 'invalid' },
|
||||||
|
|
@ -114,6 +124,7 @@ describe('cp.config', function()
|
||||||
it('accepts valid run_panel config', function()
|
it('accepts valid run_panel config', function()
|
||||||
local valid_config = {
|
local valid_config = {
|
||||||
run_panel = {
|
run_panel = {
|
||||||
|
ansi = false,
|
||||||
diff_mode = 'git',
|
diff_mode = 'git',
|
||||||
next_test_key = 'j',
|
next_test_key = 'j',
|
||||||
prev_test_key = 'k',
|
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