Add basic Lua ALE functions and test coverage

Ensure that basic ALE functions `ale.var`, `ale.escape`, and `ale.env`
are available in Lua. Cover all Lua code so far with busted tests,
fixing bugs where ALE variables can be set with Boolean values instead
of numbers. Document all functionality so far.
This commit is contained in:
w0rp
2025-03-21 23:37:35 +00:00
parent 06f4b6fe25
commit bd591d47f2
16 changed files with 944 additions and 105 deletions

View File

@@ -1,96 +1,95 @@
local ale = require("ale")
local module = {}
local ale_type_to_diagnostic_severity = {
E = vim.diagnostic.severity.ERROR,
W = vim.diagnostic.severity.WARN,
I = vim.diagnostic.severity.INFO
local diagnostic_severity_map = {
E = vim.diagnostic.severity.ERROR,
W = vim.diagnostic.severity.WARN,
I = vim.diagnostic.severity.INFO
}
-- Equivalent to ale#Var, only we can't error on missing global keys.
module.aleVar = function(buffer, key)
key = "ale_" .. key
local exists, value = pcall(vim.api.nvim_buf_get_var, buffer, key)
if exists then
return value
end
return vim.g[key]
end
module.sendAleResultsToDiagnostics = function(buffer, loclist)
local diagnostics = {}
-- Convert all the ALE loclist items to the shape that Neovim's diagnostic
-- API is expecting.
for _, location in ipairs(loclist) do
if location.bufnr == buffer then
table.insert(
diagnostics,
-- All line numbers from ALE are 1-indexed, but all line numbers
-- in the diagnostics API are 0-indexed, so we have to subtract 1
-- to make this work.
{
lnum = location.lnum - 1,
-- Ending line number, or if we don't have one, just make it the same
-- as the starting line number
end_lnum = (location.end_lnum or location.lnum) - 1,
-- Which column does the error start on?
col = math.max((location.col or 1) - 1, 0),
-- end_col does *not* appear to need 1 subtracted, so we don't.
end_col = location.end_col,
-- Which severity: error, warning, or info?
severity = ale_type_to_diagnostic_severity[location.type] or "E",
-- An error code
code = location.code,
-- The error message
message = location.text,
-- e.g. "rubocop"
source = location.linter_name,
}
)
end
end
local virtualtext_enabled_set = {
['all'] = true,
['2'] = true,
-- A map of all possible values that we can consider virtualtext enabled for
-- from ALE's setting.
local virtualtext_enabled_set = {
["all"] = true,
["2"] = true,
[2] = true,
['current'] = true,
['1'] = true,
["current"] = true,
["1"] = true,
[1] = true,
}
[true] = true,
}
local set_signs = module.aleVar(buffer, 'set_signs')
local sign_priority = module.aleVar(buffer, 'sign_priority')
local signs
---Send diagnostics to the Neovim diagnostics API
---@param buffer number The buffer number to retreive the variable for.
---@param loclist table The loclist array to report as diagnostics.
---@return nil
module.send = function(buffer, loclist)
local diagnostics = {}
if set_signs == 1 and sign_priority then
-- If signs are enabled, set the priority for them.
local local_cfg = { priority = sign_priority }
local global_cfg = vim.diagnostic.config().signs
if type(global_cfg) == 'boolean' then
signs = local_cfg
elseif type(global_cfg) == 'table' then
signs = vim.tbl_extend('force', global_cfg, local_cfg)
else
signs = function(...)
local calculated = global_cfg(...)
return vim.tbl_extend('force', calculated, local_cfg)
end
-- Convert all the ALE loclist items to the shape that Neovim's diagnostic
-- API is expecting.
for _, location in ipairs(loclist) do
if location.bufnr == buffer then
table.insert(
diagnostics,
-- All line numbers from ALE are 1-indexed, but all line
-- numbers in the diagnostics API are 0-indexed, so we have to
-- subtract 1 to make this work.
{
lnum = location.lnum - 1,
-- Ending line number, or if we don't have one, just make
-- it the same as the starting line number
end_lnum = (location.end_lnum or location.lnum) - 1,
-- Which column does the error start on?
col = math.max((location.col or 1) - 1, 0),
-- end_col does not appear to need 1 subtracted.
end_col = location.end_col,
-- Which severity: error, warning, or info?
severity = diagnostic_severity_map[location.type] or "E",
-- An error code
code = location.code,
-- The error message
message = location.text,
-- e.g. "rubocop"
source = location.linter_name,
}
)
end
end
end
vim.diagnostic.set(
vim.api.nvim_create_namespace('ale'),
buffer,
diagnostics,
{
virtual_text = virtualtext_enabled_set[vim.g.ale_virtualtext_cursor] ~= nil,
signs = signs,
}
)
local set_signs = ale.var(buffer, "set_signs")
local sign_priority = ale.var(buffer, "sign_priority")
local signs
if (set_signs == 1 or set_signs == true) and sign_priority then
-- If signs are enabled, set the priority for them.
local local_cfg = { priority = sign_priority }
local global_cfg = vim.diagnostic.config().signs
if type(global_cfg) == "boolean" then
signs = local_cfg
elseif type(global_cfg) == "table" then
signs = vim.tbl_extend("force", global_cfg, local_cfg)
else
-- If a global function is defined, then define a function
-- that calls that function when Neovim calls our function.
signs = function(...)
return vim.tbl_extend("force", global_cfg(...), local_cfg)
end
end
end
vim.diagnostic.set(
vim.api.nvim_create_namespace("ale"),
buffer,
diagnostics,
{
virtual_text =
virtualtext_enabled_set[vim.g.ale_virtualtext_cursor] ~= nil,
signs = signs,
}
)
end
return module

View File

@@ -1,19 +1,19 @@
local ale = {}
local global_settings = setmetatable({}, {
__index = function (_, key)
__index = function(_, key)
return vim.g['ale_' .. key]
end,
__newindex = function (_, key, value)
__newindex = function(_, key, value)
vim.g['ale_' .. key] = value
end
})
local buffer_settings = setmetatable({}, {
__index = function (_, key)
__index = function(_, key)
return vim.b['ale_' .. key]
end,
__newindex = function (_, key, value)
__newindex = function(_, key, value)
vim.b['ale_' .. key] = value
end
})
@@ -30,17 +30,79 @@ ale.set_buffer = function(c)
end
end
---(when called) Set global ALE settings, just like ale.setup.global.
---@class ALESetup
---@field global fun(c: table): nil -- Set global ALE settings.
---@field buffer fun(c: table): nil -- Set buffer-local ALE settings.
---@overload fun(c: table): nil
---@type ALESetup
ale.setup = setmetatable({
global = function(c)
ale.set_global(c)
end,
buffer = function(c)
ale.set_buffer(c)
end,
---Set global ALE settings.
---@param c table The table of ALE settings to set.
---@return nil
global = function(c)
ale.set_global(c)
end,
---Set buffer-local ALE settings.
---@param c table The table of ALE settings to set.
---@return nil
buffer = function(c)
ale.set_buffer(c)
end,
}, {
__call = function(self, c)
self.global(c)
end,
__call = function(self, c)
self.global(c)
end,
})
---Get an ALE variable for a buffer (first) or globally (second)
---@param buffer number The buffer number to retreive the variable for.
---@param variable_name string The variable to retrieve.
---@return any value The value for the ALE variable
ale.var = function(buffer, variable_name)
variable_name = "ale_" .. variable_name
local exists, value = pcall(vim.api.nvim_buf_get_var, buffer, variable_name)
if exists then
return value
end
return vim.g[variable_name]
end
---Escape a string for use in a shell command
---@param str string The string to escape.
---@return string escaped The escaped string.
ale.escape = function(str)
local shell = vim.fn.fnamemodify(vim.o.shell, ":t")
if shell:lower() == "cmd.exe" then
local step1
if str:find(" ") then
step1 = '"' .. str:gsub('"', '""') .. '"'
else
step1 = str:gsub("([&|<>^])", "^%1")
end
local percent_subbed = step1:gsub("%%", "%%%%")
return percent_subbed
end
return vim.fn.shellescape(str)
end
---Create a prefix for a shell command for adding environment variables.
---@param variable_name string The environment variable name.
---@param value string The value to set for the environment variable.
---@return string prefix The shell code for prefixing a command.
ale.env = function(variable_name, value)
if vim.fn.has("win32") then
return "set " .. ale.escape(variable_name .. "=" .. value) .. " && "
end
return variable_name .. "=" .. ale.escape(value) .. " "
end
return ale

View File

@@ -2,7 +2,9 @@ local module = {}
module.start = function(config)
-- Neovim's luaeval sometimes adds a Boolean key to table we need to remove.
if config.init_options[true] ~= nil then
if type(config.init_options) == "table"
and config.init_options[true] ~= nil
then
config.init_options[true] = nil
end