feat: added `opts.headless` to control ansi output when running headless

This commit is contained in:
Folke Lemaitre 2024-06-26 21:38:28 +02:00
parent 93b3a77286
commit a0a51c06c2
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
5 changed files with 153 additions and 10 deletions

View File

@ -130,6 +130,17 @@ M.defaults = {
},
},
},
-- Output options for headless mode
headless = {
-- show the output from process commands like git
process = true,
-- show log messages
log = true,
-- show task start/end
task = true,
-- use ansi colors
colors = true,
},
diff = {
-- diff command <d> can be one of:
-- * browser: opens the github compare view. Note that this is always mapped to <K> as well,

View File

@ -48,6 +48,7 @@ local uv = vim.uv
---@field cwd? string
---@field on_line? fun(string)
---@field on_exit? fun(ok:boolean, output:string)
---@field on_data? fun(string)
---@field timeout? number
---@field env? table<string,string>
@ -145,6 +146,11 @@ function M.spawn(cmd, opts)
assert(not err, err)
if data then
if opts.on_data then
vim.schedule(function()
opts.on_data(data)
end)
end
output = output .. data:gsub("\r\n", "\n")
local lines = vim.split(vim.trim(output:gsub("\r$", "")):gsub("[^\n\r]+\r", ""), "\n")

View File

@ -1,5 +1,9 @@
local Async = require("lazy.async")
local Config = require("lazy.core.config")
local Process = require("lazy.manage.process")
local Terminal = require("lazy.terminal")
local colors = Config.options.headless.colors
---@class LazyTaskDef
---@field skip? fun(plugin:LazyPlugin, opts?:TaskOptions):any?
@ -96,6 +100,10 @@ function Task:_start(task)
assert(not self:has_started(), "task already started")
assert(not self:has_ended(), "task already done")
if Config.headless() and Config.options.headless.task then
self:log("Running task " .. self.name, vim.log.levels.INFO)
end
self._started = vim.uv.hrtime()
---@async
self._running = Async.run(function()
@ -122,6 +130,27 @@ function Task:log(msg, level)
---@cast msg string
table.insert(self._log, { msg = msg, level = level })
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
if Config.headless() then
self:headless()
end
end
function Task:headless()
if not Config.options.headless.log then
return
end
local msg = self._log[#self._log]
if not msg or msg.level == vim.log.levels.TRACE then
return
end
local map = {
[vim.log.levels.ERROR] = Terminal.red,
[vim.log.levels.WARN] = Terminal.yellow,
[vim.log.levels.INFO] = Terminal.blue,
}
local color = Config.options.headless.colors and map[msg.level]
io.write(Terminal.prefix(color and color(msg.msg) or msg.msg, self:prefix()))
io.write("\n")
end
---@param msg string|string[]
@ -143,6 +172,10 @@ function Task:_done()
return
end
if Config.headless() and Config.options.headless.task then
local ms = math.floor(self:time() + 0.5)
self:log("Finished task " .. self.name .. " in " .. ms .. "ms", vim.log.levels.INFO)
end
self._ended = vim.uv.hrtime()
if self._opts.on_done then
self._opts.on_done(self)
@ -172,8 +205,12 @@ function Task:spawn(cmd, opts)
local on_line = opts.on_line
local on_exit = opts.on_exit
local headless = Config.headless() and Config.options.headless.process
function opts.on_line(line)
self:log(line, vim.log.levels.TRACE)
if not headless then
return self:log(line, vim.log.levels.TRACE)
end
if on_line then
pcall(on_line, line)
end
@ -182,18 +219,36 @@ function Task:spawn(cmd, opts)
local running = true
---@param output string
function opts.on_exit(ok, output)
self:log(vim.trim(output), ok and vim.log.levels.DEBUG or vim.log.levels.ERROR)
if not headless then
self:log(vim.trim(output), ok and vim.log.levels.DEBUG or vim.log.levels.ERROR)
end
if on_exit then
pcall(on_exit, ok, output)
end
running = false
end
if headless then
opts.on_data = function(data)
-- prefix with plugin name
local prefix = self:prefix()
io.write(Terminal.prefix(data, prefix))
end
end
Process.spawn(cmd, opts)
while running do
coroutine.yield()
end
end
function Task:prefix()
local plugin = "[" .. self.plugin.name .. "] "
local task = string.rep(" ", 20 - #(self.name .. self.plugin.name)) .. self.name
return colors and Terminal.magenta(plugin) .. Terminal.cyan(task) .. Terminal.bright_black(" | ")
or plugin .. " " .. task .. " | "
end
function Task:wait()
while self:is_running() do
vim.wait(10)

71
lua/lazy/terminal.lua Normal file
View File

@ -0,0 +1,71 @@
---@class Ansi: table<string, fun(string):string>
local M = {}
M.colors = {
reset = "\27[0m",
black = "\27[30m",
red = "\27[31m",
green = "\27[32m",
yellow = "\27[33m",
blue = "\27[34m",
magenta = "\27[35m",
cyan = "\27[36m",
white = "\27[37m",
bright_black = "\27[90m",
bright_red = "\27[91m",
bright_green = "\27[92m",
bright_yellow = "\27[93m",
bright_blue = "\27[94m",
bright_magenta = "\27[95m",
bright_cyan = "\27[96m",
bright_white = "\27[97m",
}
function M.color(text, color)
return M.colors[color] .. text .. M.colors.reset
end
-- stylua: ignore start
function M.black(text) return M.color(text, "black") end
function M.red(text) return M.color(text, "red") end
function M.green(text) return M.color(text, "green") end
function M.yellow(text) return M.color(text, "yellow") end
function M.blue(text) return M.color(text, "blue") end
function M.magenta(text) return M.color(text, "magenta") end
function M.cyan(text) return M.color(text, "cyan") end
function M.white(text) return M.color(text, "white") end
function M.bright_black(text) return M.color(text, "bright_black") end
function M.bright_red(text) return M.color(text, "bright_red") end
function M.bright_green(text) return M.color(text, "bright_green") end
function M.bright_yellow(text) return M.color(text, "bright_yellow") end
function M.bright_blue(text) return M.color(text, "bright_blue") end
function M.bright_magenta(text) return M.color(text, "bright_magenta") end
function M.bright_cyan(text) return M.color(text, "bright_cyan") end
function M.bright_white(text) return M.color(text, "bright_white") end
-- stylua: ignore end
---@param data string
---@param prefix string
function M.prefix(data, prefix)
-- Normalize Windows-style newlines to simple newlines
data = data:gsub("\r\n", "\n")
-- Handle prefix for the first line, if data starts immediately
data = prefix .. data
-- Prefix new lines ensuring not to double prefix if a line starts with \r
data = data:gsub("(\n)([^\r])", "%1" .. prefix .. "%2")
-- Handle carriage returns properly to avoid double prefixing
-- Replace any \r not followed by \n with \r, then add a prefix only if the following character isn't the start of our prefix
data = data:gsub("\r([^\n])", function(nextChar)
if nextChar:sub(1, #prefix) == prefix then
return "\r" .. nextChar
else
return "\r" .. prefix .. nextChar
end
end)
return data
end
return M

View File

@ -6,22 +6,22 @@ for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
-- -- Bootstrap lazy.nvim
-- local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
-- if not (vim.uv or vim.loop).fs_stat(lazypath) then
-- local lazyrepo = "https://github.com/folke/lazy.nvim.git"
-- vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
-- end
-- vim.opt.rtp:prepend(lazypath)
vim.opt.rtp:prepend(".")
vim.o.loadplugins = true -- enable since nvim -l disables plugins
-- Setup lazy.nvim
require("lazy").setup({
"lunarmodules/busted", -- add busted
spec = {
"lunarmodules/busted", -- add busted
},
rocks = { hererocks = true },
})
local Config = require("lazy.core.config")
-- disable termnial output for the tests
Config.options.headless = {}
-- run busted
return pcall(require("busted.runner"), {
standalone = false,