Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve colored output #26

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 109 additions & 8 deletions lua/neotest-go/color.lua
Original file line number Diff line number Diff line change
@@ -1,16 +1,117 @@
local M = {}

-- Highlight some parts of the test output
function M.highlight_output(output)
local fmt = string.format
local colors_patterns = require("neotest-go.patterns").colors

local term = os.getenv("COLORTERM")
local fullcolor = (term == "truecolor") or (term == "24bit")

-- Function: hex_to_rgb(arg)
-- Argument: Hex string value in the form '#cccccc' or 'cccccc'
-- HEX shorthand is supported
-- Returns: Three RGB values
-- Red value from 0-255
-- Green value from 0-255
-- Blue value from 0-255
-- Source: forum.rainmeter.net/viewtopic.php?t=29419
local function hex_to_rgb(hexArg)
hexArg = hexArg:gsub("#", "")
Davincible marked this conversation as resolved.
Show resolved Hide resolved
if string.len(hexArg) == 3 then
return tonumber("0x" .. hexArg:sub(1, 1)) * 17,
tonumber("0x" .. hexArg:sub(2, 2)) * 17,
tonumber("0x" .. hexArg:sub(3, 3)) * 17
elseif string.len(hexArg) == 6 then
return tonumber("0x" .. hexArg:sub(1, 2)),
tonumber("0x" .. hexArg:sub(3, 4)),
tonumber("0x" .. hexArg:sub(5, 6))
else
return 0, 0, 0
end
end

-- Get color from config, of default if not present in config.
-- @param config {}
-- @param section string
-- @param color_class string
-- @return string
local function get_color(config, section, index, color_class)
local color = colors_patterns[section][color_class]
if
config[section] ~= nil
or config[section][color_class] ~= nil and #config[section][color_class] ~= 0
then
color = config[section][color_class]
end
if type(color) ~= "table" then
return color
end
return color[index]
end

-- Add 24bit console color to a line.
-- @param line string
-- @param hex_color string
-- @return string
local function add_24bit_color(line, hex_color)
local r, g, b = hex_to_rgb(hex_color)
local start_code = fmt("[38;2;%d;%d;%dm", r, g, b)
local end_code = ""
return start_code .. line .. end_code
end

-- Add 8bit term color to a line.
--@param line string
--@param color string
--@return string
local function add_8bit_color(line, color)
local start_code = fmt("[%dm", color)
local end_code = ""
return start_code .. line .. end_code
end

-- Colorize a line.
-- @param line string
-- @param config table
-- @param section string
-- @return string
local function add_color(section, line, config, i)
if fullcolor then
local color = get_color(config, section, i, "gui")
return add_24bit_color(line, color)
end
local color = get_color(config, section, i, "term")
return add_8bit_color(line, color)
end

local function is_table(a)
return type(a) == "table"
end

-- Highlight output.
-- @param output string
-- @param config table
-- @return string
function M.highlight_output(output, opts)
if not output then
return output
end
if string.find(output, "FAIL") then
output = output:gsub("^", ""):gsub("$", "")
elseif string.find(output, "PASS") then
output = output:gsub("^", ""):gsub("$", "")
elseif string.find(output, "SKIP") then
output = output:gsub("^", ""):gsub("$", "")
-- Fetch patterns
local config = colors_patterns
if is_table(opts) and opts.colors ~= nil then
config = opts.colors
end
-- Iterate over all patterns to colorize
for name, c in pairs(colors_patterns) do
local res = { output:find(c.pattern) }
if #res > 2 then
-- Colorize all groups in pattern
for i, match_group in ipairs(vim.list_slice(res, 3)) do
local colorized = add_color(name, match_group, config, i)
output = output:gsub(vim.pesc(match_group), vim.pesc(colorized))
end
elseif #res > 0 then
output = add_color(name, output, config)
end
end
return output
end
Expand Down
86 changes: 86 additions & 0 deletions lua/neotest-go/patterns.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,93 @@
local termcolors = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these come from a user's config, but default to these values to start, so they can be easily overridden?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. What do you think would be the best behavior here;

  1. if a user specifies a custom pattern, replace all patterns with the users' patterns
  2. if a user specifies a custom pattern, add on to default patterns, and overwrite the default pattern if they have the same name

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Davincible I think in most cases plugin authors use vim.tbl_deep_extend i.e. we only ever replace something that has been specifically changed by the user so essentially option 2

black = 30,
red = 31,
green = 32,
yellow = 33,
blue = 34,
magenta = 35,
cyan = 36,
white = 37,
}

local guicolors = {
green = "#14D000",
red = "#cc0000",
blue = "#729fcf",
cyan = "#20b2aa",
magenta = "#a474dc",
grey = "#777777",
yellow = "#deb887",
orange = "#ff7373",
}

local patterns = {
testfile = "^%s%s%s%s(.*_test.go):(%d+): ",
testlog = "^%s%s%s%s%s%s%s%s",
error = { "error" },
-- Patterns to colorize in output
colors = {
pass = { pattern = "^%s*---%s+PASS:", gui = guicolors.green, term = termcolors.red },
fail = { pattern = "^---%s+FAIL:", gui = guicolors.red, term = termcolors.green },
skip = { pattern = "^---%s+SKIP:", gui = guicolors.blue, term = termcolors.yellow },
build = { pattern = "^===%s+BUILD", gui = guicolors.yellow, term = termcolors.yellow },
comment = { pattern = "^%s*#", gui = guicolors.grey, term = termcolors.blue },
run = {
pattern = "^(===%s+RUN)%s+(.*)",
gui = { guicolors.grey, guicolors.yellow },
term = { termcolors.blue, termcolors.blue },
},
signal = {
pattern = "^(%[signal)%s(.*)(:.*%])",
gui = { guicolors.grey, guicolors.blue, guicolors.grey },
term = { termcolors.blue, termcolors.red, termcolors.blue },
},
panic = {
pattern = "^%s+(panic:)%s+(.*)",
gui = { guicolors.red, guicolors.orange },
term = { termcolors.red, termcolors.red },
},
panic_recovered = {
pattern = "^(panic:)%s+(.*)%s(%[.*%])",
gui = { guicolors.red, guicolors.orange, guicolors.blue },
term = { termcolors.red, termcolors.red, termcolors.blue },
},
go_routine = {
pattern = "^(goroutine)%s(%d+)%s(%[.*])(:)",
gui = { guicolors.grey, guicolors.magenta, guicolors.blue, guicolors.grey },
term = { termcolors.blue, termcolors.blue, termcolors.magenta, termcolors.blue },
},
file = {
pattern = "^%s+(.*.go)(:%d+)",
gui = { guicolors.cyan, guicolors.magenta },
term = { termcolors.cyan, termcolors.magenta },
},
file_column = {
pattern = "^%s*(.*.go)(:%d+)(:%d+)",
gui = { guicolors.cyan, guicolors.magenta, guicolors.blue },
term = { termcolors.cyan, termcolors.magenta, termcolors.blue },
},
file_panic = {
pattern = "^%s+(.*.go)(:%d+)%s+(%+0x%w+)",
gui = { guicolors.cyan, guicolors.magenta, guicolors.grey },
term = { termcolors.cyan, termcolors.magenta, termcolors.blue },
},
-- Color error messages from github.com/stretchr/testify
testify_error_trace = {
pattern = "^%s+(Error Trace:)%s+(.*)",
gui = { guicolors.grey, guicolors.grey },
term = { termcolors.yellow, termcolors.blue },
},
testify_error = {
pattern = "^%s+(Error:)%s+(.*)",
gui = { guicolors.red, guicolors.red },
term = { termcolors.yellow, termcolors.yellow },
},
testify_test = {
pattern = "^%s+(Test:)%s+(.*)",
gui = { guicolors.grey, guicolors.yellow },
term = { termcolors.blue, termcolors.blue },
},
},
}

return patterns