183 lines
5.1 KiB
Lua
183 lines
5.1 KiB
Lua
---@class AnsiParseResult
|
|
---@field lines string[]
|
|
---@field highlights table[]
|
|
|
|
local M = {}
|
|
|
|
---@param raw_output string|table
|
|
---@return string
|
|
function M.bytes_to_string(raw_output)
|
|
if type(raw_output) == 'string' then
|
|
return raw_output
|
|
end
|
|
return table.concat(vim.tbl_map(string.char, raw_output))
|
|
end
|
|
|
|
---@param text string
|
|
---@return AnsiParseResult
|
|
function M.parse_ansi_text(text)
|
|
local clean_text = text:gsub('\027%[[%d;]*[a-zA-Z]', '')
|
|
local lines = vim.split(clean_text, '\n', { plain = true, trimempty = false })
|
|
|
|
local highlights = {}
|
|
local line_num = 0
|
|
local col_pos = 0
|
|
local current_style = nil
|
|
|
|
local i = 1
|
|
while i <= #text do
|
|
local ansi_start, ansi_end, code, cmd = text:find('\027%[([%d;]*)([a-zA-Z])', i)
|
|
|
|
if ansi_start then
|
|
if ansi_start > i then
|
|
local segment = text:sub(i, ansi_start - 1)
|
|
if current_style then
|
|
local start_line = line_num
|
|
local start_col = col_pos
|
|
|
|
for char in segment:gmatch('.') do
|
|
if char == '\n' then
|
|
if col_pos > start_col then
|
|
table.insert(highlights, {
|
|
line = start_line,
|
|
col_start = start_col,
|
|
col_end = col_pos,
|
|
highlight_group = current_style,
|
|
})
|
|
end
|
|
line_num = line_num + 1
|
|
start_line = line_num
|
|
col_pos = 0
|
|
start_col = 0
|
|
else
|
|
col_pos = col_pos + 1
|
|
end
|
|
end
|
|
|
|
if col_pos > start_col then
|
|
table.insert(highlights, {
|
|
line = start_line,
|
|
col_start = start_col,
|
|
col_end = col_pos,
|
|
highlight_group = current_style,
|
|
})
|
|
end
|
|
else
|
|
for char in segment:gmatch('.') do
|
|
if char == '\n' then
|
|
line_num = line_num + 1
|
|
col_pos = 0
|
|
else
|
|
col_pos = col_pos + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if cmd == 'm' then
|
|
local logger = require('cp.log')
|
|
logger.log('Found color code: "' .. code .. '"')
|
|
current_style = M.ansi_code_to_highlight(code)
|
|
logger.log('Mapped to highlight: ' .. (current_style or 'nil'))
|
|
end
|
|
i = ansi_end + 1
|
|
else
|
|
local segment = text:sub(i)
|
|
if current_style and segment ~= '' then
|
|
local start_line = line_num
|
|
local start_col = col_pos
|
|
|
|
for char in segment:gmatch('.') do
|
|
if char == '\n' then
|
|
if col_pos > start_col then
|
|
table.insert(highlights, {
|
|
line = start_line,
|
|
col_start = start_col,
|
|
col_end = col_pos,
|
|
highlight_group = current_style,
|
|
})
|
|
end
|
|
line_num = line_num + 1
|
|
start_line = line_num
|
|
col_pos = 0
|
|
start_col = 0
|
|
else
|
|
col_pos = col_pos + 1
|
|
end
|
|
end
|
|
|
|
if col_pos > start_col then
|
|
table.insert(highlights, {
|
|
line = start_line,
|
|
col_start = start_col,
|
|
col_end = col_pos,
|
|
highlight_group = current_style,
|
|
})
|
|
end
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
return {
|
|
lines = lines,
|
|
highlights = highlights,
|
|
}
|
|
end
|
|
|
|
---@param code string
|
|
---@return string?
|
|
function M.ansi_code_to_highlight(code)
|
|
local color_map = {
|
|
-- Simple colors
|
|
['31'] = 'CpAnsiRed',
|
|
['32'] = 'CpAnsiGreen',
|
|
['33'] = 'CpAnsiYellow',
|
|
['34'] = 'CpAnsiBlue',
|
|
['35'] = 'CpAnsiMagenta',
|
|
['36'] = 'CpAnsiCyan',
|
|
['37'] = 'CpAnsiWhite',
|
|
['91'] = 'CpAnsiBrightRed',
|
|
['92'] = 'CpAnsiBrightGreen',
|
|
['93'] = 'CpAnsiBrightYellow',
|
|
-- Bold colors (G++ style)
|
|
['01;31'] = 'CpAnsiBrightRed', -- bold red
|
|
['01;32'] = 'CpAnsiBrightGreen', -- bold green
|
|
['01;33'] = 'CpAnsiBrightYellow', -- bold yellow
|
|
['01;34'] = 'CpAnsiBrightBlue', -- bold blue
|
|
['01;35'] = 'CpAnsiBrightMagenta', -- bold magenta
|
|
['01;36'] = 'CpAnsiBrightCyan', -- bold cyan
|
|
-- Bold/formatting only
|
|
['01'] = 'CpAnsiBold', -- bold text
|
|
['1'] = 'CpAnsiBold', -- bold text (alternate)
|
|
-- Reset codes
|
|
['0'] = nil,
|
|
[''] = nil,
|
|
}
|
|
return color_map[code]
|
|
end
|
|
|
|
function M.setup_highlight_groups()
|
|
local groups = {
|
|
CpAnsiRed = { fg = vim.g.terminal_color_1 },
|
|
CpAnsiGreen = { fg = vim.g.terminal_color_2 },
|
|
CpAnsiYellow = { fg = vim.g.terminal_color_3 },
|
|
CpAnsiBlue = { fg = vim.g.terminal_color_4 },
|
|
CpAnsiMagenta = { fg = vim.g.terminal_color_5 },
|
|
CpAnsiCyan = { fg = vim.g.terminal_color_6 },
|
|
CpAnsiWhite = { fg = vim.g.terminal_color_7 },
|
|
CpAnsiBrightRed = { fg = vim.g.terminal_color_9 },
|
|
CpAnsiBrightGreen = { fg = vim.g.terminal_color_10 },
|
|
CpAnsiBrightYellow = { fg = vim.g.terminal_color_11 },
|
|
CpAnsiBrightBlue = { fg = vim.g.terminal_color_12 },
|
|
CpAnsiBrightMagenta = { fg = vim.g.terminal_color_13 },
|
|
CpAnsiBrightCyan = { fg = vim.g.terminal_color_14 },
|
|
CpAnsiBold = { bold = true },
|
|
}
|
|
|
|
for name, opts in pairs(groups) do
|
|
vim.api.nvim_set_hl(0, name, opts)
|
|
end
|
|
end
|
|
|
|
return M
|