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

feature: Color on the output #206

Closed
2 tasks done
jaymorelli96 opened this issue Oct 18, 2024 · 6 comments
Closed
2 tasks done

feature: Color on the output #206

jaymorelli96 opened this issue Oct 18, 2024 · 6 comments
Labels
enhancement New feature or request

Comments

@jaymorelli96
Copy link
Contributor

Did you check docs and existing issues?

Is your feature request related to a problem? Please describe.

Hi, I don't think neotest-golang supports color in the output like neotest-go does. I would love to contribute but I may need a little help. If it does support how can I configure it?

Describe the solution you'd like to see.

This is the output of neotest-go:
image

This is the output of neotest-golang:
image

Describe alternatives you've considered.

I guess would be to do something similar of what neotest-go does in the output.

Additional context

No response

@jaymorelli96 jaymorelli96 added the enhancement New feature or request label Oct 18, 2024
@fredrikaverpil
Copy link
Owner

fredrikaverpil commented Oct 19, 2024

Hi @jaymorelli96 👋 I haven't looked at how neotest-go does this, but if they do it I'm sure it's possible 😄

As long as this can be done reliably and without affecting performance too much, I'm open to accept a PR on this. 👍
Could be good to add a configuration option so to opt-out of this, in case of experiencing issues.

There are a couple of places you can implement this in, based on how you plan to do this. But I guess this is the file you want to look into first, which is responsible for processing the test output into neotest.Result format: https://github.com/fredrikaverpil/neotest-golang/blob/ea1c3a77f147e29583cd756b44ee69d9007bebba/lua/neotest-golang/process.lua

to_neotest_result

This function converts from the internal data structure onto Neotest's neotest.Result format, which is what is used and shown in/by Neotest:

--- Populate final Neotest results based on internal test result data.
--- @param res table<string, TestData>
--- @return table<string, neotest.Result>
function M.to_neotest_result(res)
--- Neotest results.
--- @type table<string, neotest.Result>
local neotest_result = {}
-- populate all test results onto the Neotest format.
for pos_id, test_data in pairs(res) do
local test_output_path = vim.fs.normalize(async.fn.tempname())
async.fn.writefile(test_data.gotest_data.output, test_output_path)
neotest_result[pos_id] = {
status = test_data.status,
errors = test_data.errors,
output = test_output_path, -- NOTE: could be slow when running many tests?
}
end
return neotest_result
end

This is basically the last touchpoint in the adapter before returning to Neotest. If you add a vim.notify(vim.inspect(neotest_result[pos_id])) here, you'll get:

{
  errors = {},
  output = "/var/folders/zp/9p20kb0s7n9chkhj9t3j7ymc0000gn/T/fredrik.fredrik/C2Z590/4",
  status = "passed"
}

This means that if the text in this file contains colors, this colorized text will be passed on to Neotest (and shown like in your screenshots).

decorate_with_go_test_results

Most likely, this is a better place to start working, as you can more easily identify any output related to an error and you can also see how I perform other regexps here.

--- Decorate the internal test result data with data from the 'go test' output.
--- @param res table<string, TestData>
--- @param gotest_output table
--- @return table<string, TestData>
function M.decorate_with_go_test_results(res, gotest_output)
for pos_id, test_data in pairs(res) do
for _, line in ipairs(gotest_output) do
if
line.Package == test_data.gotest_data.pkg
and (
line.Test == test_data.gotest_data.name
or lib.string.starts_with(
line.Test,
test_data.gotest_data.name .. "/"
)
)
then
-- record test status
if line.Action == "pass" then
test_data.status = "passed"
elseif line.Action == "fail" then
test_data.status = "failed"
elseif line.Action == "output" then
test_data.gotest_data.output =
vim.list_extend(test_data.gotest_data.output, { line.Output })
-- determine test filename
local test_filename = "_test.go" -- approximate test filename
if test_data.neotest_data ~= nil then
-- node data is available, get the exact test filename
local test_filepath = test_data.neotest_data.path
test_filename = vim.fn.fnamemodify(test_filepath, ":t")
end
-- search for error message and line number
local matched_line_number =
string.match(line.Output, test_filename .. ":(%d+):")
if matched_line_number ~= nil then
local line_number = tonumber(matched_line_number)
local message =
string.match(line.Output, test_filename .. ":%d+: (.*)")
if line_number ~= nil and message ~= nil then
table.insert(test_data.errors, {
line = line_number - 1, -- neovim lines are 0-indexed
message = message,
})
end
end
end
end
end
end
return res
end

For example, you could use string.match so to try and find strings such as --- FAIL: and then colorize them.

It's the test_data.gotest_data.output which contains the test output.

@fredrikaverpil
Copy link
Owner

fredrikaverpil commented Oct 19, 2024

I might add that in order to avoid too much of a spaghetti bowl, you might want to introduce this as its own function, something like:

function M.aggregate_data(tree, gotest_output, golist_output)
  local res = M.gather_neotest_data_and_set_defaults(tree)
  res =
    M.decorate_with_go_package_and_test_name(res, gotest_output, golist_output)
  res = M.decorate_with_go_test_results(res, gotest_output)
+  if options.get().colorize_test_output == true then
+    res = M.colorize_go_test_results(res)
+  end
  return res
end

But it might be more performant to introduce this inside the loop we already iterate through:

function M.decorate_with_go_test_results(res, gotest_output)
+ local colorize = options.get().colorize_test_output
...
        if line.Action == "pass" then
          test_data.status = "passed"
        elseif line.Action == "fail" then
          test_data.status = "failed"
+        elseif line.Action == "output" and colorize then
+          test_data.gotest_data.output =
+            vim.list_extend(test_data.gotest_data.output, { colorize_go_test_results(line.Output) })
        elseif line.Action == "output" then
          test_data.gotest_data.output =
            vim.list_extend(test_data.gotest_data.output, { line.Output })
...

@jaymorelli96
Copy link
Contributor Author

Wow, that is an amazing help @fredrikaverpil 😄 I really appreciate the fast reply and all the help pointing me in the right direction! I gotta be honest I am not much familiar with lua, let alone creating a plugin, but i am very interested. I will spend the next few days to get a bit more comfortable with it and then I will start playing around with the implementation. As soon as I have some news I will let you know!

@fredrikaverpil
Copy link
Owner

Ok! 😊👍

@jaymorelli96
Copy link
Contributor Author

jaymorelli96 commented Oct 25, 2024

Hi @fredrikaverpil, only got time to start working on it this morning. Got to work!
image

I am doing inside the M.decorate_with_go_test_results and just doing a simple string match similar on how neotest-go is doing. I will clean up (create a function for it, set up the opts, and so on) a bit now. I just wanted you to know that I will be working on this a bit over the next few days

It is my first time playing around with lua and neovim plugin and I must say it is pretty fun 😄

@fredrikaverpil
Copy link
Owner

Fixed by #208

@fredrikaverpil fredrikaverpil closed this as not planned Won't fix, can't repro, duplicate, stale Oct 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants