fix(ansi): 256 dynamic colors
This commit is contained in:
parent
d2bde9bad8
commit
794426402a
1 changed files with 150 additions and 52 deletions
|
|
@ -12,13 +12,81 @@ local M = {}
|
|||
|
||||
local logger = require('cp.log')
|
||||
|
||||
---@param raw_output string|table
|
||||
local dyn_hl_cache = {}
|
||||
|
||||
---@param s string|table
|
||||
---@return string
|
||||
function M.bytes_to_string(raw_output)
|
||||
if type(raw_output) == 'string' then
|
||||
return raw_output
|
||||
function M.bytes_to_string(s)
|
||||
if type(s) == 'string' then
|
||||
return s
|
||||
end
|
||||
return table.concat(vim.tbl_map(string.char, raw_output))
|
||||
return table.concat(vim.tbl_map(string.char, s))
|
||||
end
|
||||
|
||||
---@param fg table|nil
|
||||
---@param bold boolean
|
||||
---@param italic boolean
|
||||
---@return string|nil
|
||||
local function ensure_hl_for(fg, bold, italic)
|
||||
if not fg and not bold and not italic then
|
||||
return nil
|
||||
end
|
||||
|
||||
local base = 'CpAnsi'
|
||||
local suffix
|
||||
local opts = {}
|
||||
|
||||
if fg and fg.kind == 'named' then
|
||||
suffix = fg.name
|
||||
elseif fg and fg.kind == 'xterm' then
|
||||
suffix = ('X%03d'):format(fg.idx)
|
||||
local function xterm_to_hex(n)
|
||||
if n >= 0 and n <= 15 then
|
||||
local key = 'terminal_color_' .. n
|
||||
return vim.g[key]
|
||||
end
|
||||
if n >= 16 and n <= 231 then
|
||||
local c = n - 16
|
||||
local r = math.floor(c / 36) % 6
|
||||
local g = math.floor(c / 6) % 6
|
||||
local b = c % 6
|
||||
local function level(x)
|
||||
return x == 0 and 0 or 55 + 40 * x
|
||||
end
|
||||
return ('#%02x%02x%02x'):format(level(r), level(g), level(b))
|
||||
end
|
||||
local l = 8 + 10 * (n - 232)
|
||||
return ('#%02x%02x%02x'):format(l, l, l)
|
||||
end
|
||||
opts.fg = xterm_to_hex(fg.idx) or 'NONE'
|
||||
elseif fg and fg.kind == 'rgb' then
|
||||
suffix = ('Rgb%02x%02x%02x'):format(fg.r, fg.g, fg.b)
|
||||
opts.fg = ('#%02x%02x%02x'):format(fg.r, fg.g, fg.b)
|
||||
end
|
||||
|
||||
local parts = { base }
|
||||
if bold then
|
||||
table.insert(parts, 'Bold')
|
||||
end
|
||||
if italic then
|
||||
table.insert(parts, 'Italic')
|
||||
end
|
||||
if suffix then
|
||||
table.insert(parts, suffix)
|
||||
end
|
||||
local name = table.concat(parts)
|
||||
|
||||
if not dyn_hl_cache[name] then
|
||||
if bold then
|
||||
opts.bold = true
|
||||
end
|
||||
if italic then
|
||||
opts.italic = true
|
||||
end
|
||||
vim.api.nvim_set_hl(0, name, opts)
|
||||
dyn_hl_cache[name] = true
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
---@param text string
|
||||
|
|
@ -38,22 +106,7 @@ function M.parse_ansi_text(text)
|
|||
}
|
||||
|
||||
local function get_highlight_group()
|
||||
if not ansi_state.bold and not ansi_state.italic and not ansi_state.foreground then
|
||||
return nil
|
||||
end
|
||||
|
||||
local parts = { 'CpAnsi' }
|
||||
if ansi_state.bold then
|
||||
table.insert(parts, 'Bold')
|
||||
end
|
||||
if ansi_state.italic then
|
||||
table.insert(parts, 'Italic')
|
||||
end
|
||||
if ansi_state.foreground then
|
||||
table.insert(parts, ansi_state.foreground)
|
||||
end
|
||||
|
||||
return table.concat(parts)
|
||||
return ensure_hl_for(ansi_state.foreground, ansi_state.bold, ansi_state.italic)
|
||||
end
|
||||
|
||||
local function apply_highlight(start_line, start_col, end_col)
|
||||
|
|
@ -137,6 +190,7 @@ end
|
|||
|
||||
---@param ansi_state table
|
||||
---@param code_string string
|
||||
---@return nil
|
||||
function M.update_ansi_state(ansi_state, code_string)
|
||||
if code_string == '' or code_string == '0' then
|
||||
ansi_state.bold = false
|
||||
|
|
@ -146,10 +200,10 @@ function M.update_ansi_state(ansi_state, code_string)
|
|||
end
|
||||
|
||||
local codes = vim.split(code_string, ';', { plain = true })
|
||||
local idx = 1
|
||||
while idx <= #codes do
|
||||
local num = tonumber(codes[idx])
|
||||
|
||||
for _, code in ipairs(codes) do
|
||||
local num = tonumber(code)
|
||||
if num then
|
||||
if num == 1 then
|
||||
ansi_state.bold = true
|
||||
elseif num == 3 then
|
||||
|
|
@ -158,10 +212,10 @@ function M.update_ansi_state(ansi_state, code_string)
|
|||
ansi_state.bold = false
|
||||
elseif num == 23 then
|
||||
ansi_state.italic = false
|
||||
elseif num >= 30 and num <= 37 then
|
||||
elseif num and num >= 30 and num <= 37 then
|
||||
local colors = { 'Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White' }
|
||||
ansi_state.foreground = colors[num - 29]
|
||||
elseif num >= 90 and num <= 97 then
|
||||
ansi_state.foreground = { kind = 'named', name = colors[num - 29] }
|
||||
elseif num and num >= 90 and num <= 97 then
|
||||
local colors = {
|
||||
'BrightBlack',
|
||||
'BrightRed',
|
||||
|
|
@ -172,14 +226,34 @@ function M.update_ansi_state(ansi_state, code_string)
|
|||
'BrightCyan',
|
||||
'BrightWhite',
|
||||
}
|
||||
ansi_state.foreground = colors[num - 89]
|
||||
ansi_state.foreground = { kind = 'named', name = colors[num - 89] }
|
||||
elseif num == 39 then
|
||||
ansi_state.foreground = nil
|
||||
elseif num == 38 or num == 48 then
|
||||
local is_fg = (num == 38)
|
||||
local mode = tonumber(codes[idx + 1] or '')
|
||||
if mode == 5 and codes[idx + 2] then
|
||||
local pal = tonumber(codes[idx + 2]) or 0
|
||||
if is_fg then
|
||||
ansi_state.foreground = { kind = 'xterm', idx = pal }
|
||||
end
|
||||
idx = idx + 2
|
||||
elseif mode == 2 and codes[idx + 2] and codes[idx + 3] and codes[idx + 4] then
|
||||
local r = tonumber(codes[idx + 2]) or 0
|
||||
local g = tonumber(codes[idx + 3]) or 0
|
||||
local b = tonumber(codes[idx + 4]) or 0
|
||||
if is_fg then
|
||||
ansi_state.foreground = { kind = 'rgb', r = r, g = g, b = b }
|
||||
end
|
||||
idx = idx + 4
|
||||
end
|
||||
end
|
||||
|
||||
idx = idx + 1
|
||||
end
|
||||
end
|
||||
|
||||
---@return nil
|
||||
function M.setup_highlight_groups()
|
||||
local color_map = {
|
||||
Black = vim.g.terminal_color_0,
|
||||
|
|
@ -218,7 +292,6 @@ function M.setup_highlight_groups()
|
|||
for color_name, terminal_color in pairs(color_map) do
|
||||
local parts = { 'CpAnsi' }
|
||||
local opts = { fg = terminal_color or 'NONE' }
|
||||
|
||||
if combo.bold then
|
||||
table.insert(parts, 'Bold')
|
||||
opts.bold = true
|
||||
|
|
@ -228,7 +301,6 @@ function M.setup_highlight_groups()
|
|||
opts.italic = true
|
||||
end
|
||||
table.insert(parts, color_name)
|
||||
|
||||
local hl_name = table.concat(parts)
|
||||
vim.api.nvim_set_hl(0, hl_name, opts)
|
||||
end
|
||||
|
|
@ -239,4 +311,30 @@ function M.setup_highlight_groups()
|
|||
vim.api.nvim_set_hl(0, 'CpAnsiBoldItalic', { bold = true, italic = true })
|
||||
end
|
||||
|
||||
---@param text string
|
||||
---@return string[]
|
||||
function M.debug_ansi_tokens(text)
|
||||
local out = {}
|
||||
local i = 1
|
||||
while true do
|
||||
local s, e, codes, cmd = text:find('\027%[([%d;]*)([a-zA-Z])', i)
|
||||
if not s then
|
||||
break
|
||||
end
|
||||
table.insert(out, ('ESC[%s%s'):format(codes, cmd))
|
||||
i = e + 1
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param s string
|
||||
---@return string
|
||||
function M.hex_dump(s)
|
||||
local t = {}
|
||||
for i = 1, #s do
|
||||
t[#t + 1] = ('%02X'):format(s:byte(i))
|
||||
end
|
||||
return table.concat(t, ' ')
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue