mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-06 04:34:25 +08:00
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:
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -30,6 +30,7 @@ jobs:
|
||||
- '--vim-90-only'
|
||||
- '--neovim-07-only'
|
||||
- '--neovim-08-only'
|
||||
- '--lua-only'
|
||||
- '--linters-only'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
19
.luarc.json
Normal file
19
.luarc.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"diagnostics.globals": [
|
||||
"vim"
|
||||
],
|
||||
"workspace.ignoreDir": [
|
||||
"test"
|
||||
],
|
||||
"workspace.library": [
|
||||
"/usr/share/nvim/runtime/lua"
|
||||
],
|
||||
"runtime.pathStrict": true,
|
||||
"runtime.path": [
|
||||
"lua/?.lua",
|
||||
"lua/?/init.lua"
|
||||
],
|
||||
"runtime.version": "LuaJIT",
|
||||
"hint.enable": false
|
||||
}
|
||||
13
Dockerfile
13
Dockerfile
@@ -2,12 +2,10 @@ ARG TESTBED_VIM_VERSION=24
|
||||
|
||||
FROM testbed/vim:${TESTBED_VIM_VERSION}
|
||||
|
||||
RUN install_vim -tag v8.0.0027 -build \
|
||||
-tag v9.0.0297 -build \
|
||||
-tag neovim:v0.7.0 -build \
|
||||
-tag neovim:v0.8.0 -build
|
||||
|
||||
ENV PACKAGES="\
|
||||
lua5.1 \
|
||||
lua5.1-dev \
|
||||
lua5.1-busted \
|
||||
bash \
|
||||
git \
|
||||
python2 \
|
||||
@@ -19,6 +17,11 @@ ENV PACKAGES="\
|
||||
RUN apk --update add $PACKAGES && \
|
||||
rm -rf /var/cache/apk/* /tmp/* /var/tmp/*
|
||||
|
||||
RUN install_vim -tag v8.0.0027 -build \
|
||||
-tag v9.0.0297 -build \
|
||||
-tag neovim:v0.7.0 -build \
|
||||
-tag neovim:v0.8.0 -build
|
||||
|
||||
RUN pip install vim-vint==0.3.21
|
||||
|
||||
RUN git clone https://github.com/junegunn/vader.vim vader && \
|
||||
|
||||
@@ -259,7 +259,7 @@ function! ale#engine#SendResultsToNeovimDiagnostics(buffer, loclist) abort
|
||||
|
||||
" Keep the Lua surface area really small in the VimL part of ALE,
|
||||
" and just require the diagnostics.lua module on demand.
|
||||
let l:SendDiagnostics = luaeval('require("ale.diagnostics").sendAleResultsToDiagnostics')
|
||||
let l:SendDiagnostics = luaeval('require("ale.diagnostics").send')
|
||||
call l:SendDiagnostics(a:buffer, a:loclist)
|
||||
endfunction
|
||||
|
||||
|
||||
29
doc/ale.txt
29
doc/ale.txt
@@ -4453,6 +4453,7 @@ Vim autocmd names `ale#Foo` are available in the Vim context, and functions
|
||||
documented with dot names `ale.foo` are available in Lua scripts.
|
||||
|
||||
|
||||
ale.env(variable_name, value) *ale.env()*
|
||||
ale#Env(variable_name, value) *ale#Env()*
|
||||
|
||||
Given a variable name and a string value, produce a string for including in
|
||||
@@ -4464,6 +4465,18 @@ ale#Env(variable_name, value) *ale#Env()*
|
||||
'set VAR="some value" && command' # On Windows
|
||||
|
||||
|
||||
ale.escape(str) *ale.escape()*
|
||||
ale#Escape(str) *ale#Escape()*
|
||||
|
||||
Given a string, escape that string so it is ready for shell execution.
|
||||
|
||||
If the shell is detected to be `cmd.exe`, ALE will apply its own escaping
|
||||
that tries to avoid escaping strings unless absolutely necessary to avoid
|
||||
issues with Windows programs that do not properly handle quoted arguments.
|
||||
|
||||
In all other cases, ALE will call |shellescape|.
|
||||
|
||||
|
||||
ale#GetFilenameMappings(buffer, name) *ale#GetFilenameMappings()*
|
||||
|
||||
Given a `buffer` and the `name` of either a linter for fixer, return a
|
||||
@@ -4509,7 +4522,7 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()*
|
||||
is broken, or when developing ALE itself.
|
||||
|
||||
|
||||
ale.setup(config) *ale.setup*
|
||||
ale.setup(config) *ale.setup()*
|
||||
|
||||
Configure ALE global settings, which are documented in |ale-options|. For
|
||||
example: >
|
||||
@@ -4524,7 +4537,7 @@ ale.setup(config) *ale.setup*
|
||||
ALE is being configured in less ambiguous if you like.
|
||||
|
||||
|
||||
ale.setup.buffer(config) *ale.setup.buffer*
|
||||
ale.setup.buffer(config) *ale.setup.buffer()*
|
||||
|
||||
Configure ALE buffer-local settings, which are documented in |ale-options|.
|
||||
For example: >
|
||||
@@ -4534,6 +4547,18 @@ ale.setup.buffer(config) *ale.setup.buffer*
|
||||
})
|
||||
<
|
||||
|
||||
ale.var(buffer, variable_name) *ale.var()*
|
||||
ale#Var(buffer, variable_name) *ale#Var()*
|
||||
|
||||
Given a buffer number and an ALE variable name return the value of that
|
||||
if defined in the buffer, and if not defined in the buffer return the
|
||||
global value. The `ale_` prefix will be added to the Vim variable name.
|
||||
|
||||
The `ale#Var` Vim function will return errors if the variable is not defined
|
||||
in either the buffer or globally. The `ale.var` Lua function will return
|
||||
`nil` if the variable is not defined in either the buffer or globally.
|
||||
|
||||
|
||||
ale#command#CreateDirectory(buffer) *ale#command#CreateDirectory()*
|
||||
|
||||
Create a new temporary directory with a unique name, and manage that
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
26
run-tests
26
run-tests
@@ -29,6 +29,7 @@ run_neovim_07_tests=1
|
||||
run_neovim_08_tests=1
|
||||
run_vim_80_tests=1
|
||||
run_vim_90_tests=1
|
||||
run_lua_tests=1
|
||||
run_linters=1
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
@@ -46,12 +47,14 @@ while [ $# -ne 0 ]; do
|
||||
run_vim_90_tests=0
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
--neovim-only)
|
||||
run_vim_80_tests=0
|
||||
run_vim_90_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
@@ -59,6 +62,7 @@ while [ $# -ne 0 ]; do
|
||||
run_neovim_08_tests=0
|
||||
run_vim_80_tests=0
|
||||
run_vim_90_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
@@ -66,12 +70,14 @@ while [ $# -ne 0 ]; do
|
||||
run_neovim_07_tests=0
|
||||
run_vim_80_tests=0
|
||||
run_vim_90_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
--vim-only)
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
@@ -79,6 +85,7 @@ while [ $# -ne 0 ]; do
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_vim_90_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
@@ -86,6 +93,7 @@ while [ $# -ne 0 ]; do
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_vim_80_tests=0
|
||||
run_lua_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
@@ -94,6 +102,15 @@ while [ $# -ne 0 ]; do
|
||||
run_vim_90_tests=0
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_lua_tests=0
|
||||
shift
|
||||
;;
|
||||
--lua-only)
|
||||
run_vim_80_tests=0
|
||||
run_vim_90_tests=0
|
||||
run_neovim_07_tests=0
|
||||
run_neovim_08_tests=0
|
||||
run_linters=0
|
||||
shift
|
||||
;;
|
||||
--fast)
|
||||
@@ -119,6 +136,7 @@ while [ $# -ne 0 ]; do
|
||||
echo ' --vim-only Run tests only for Vim'
|
||||
echo ' --vim-80-only Run tests only for Vim 8.2'
|
||||
echo ' --vim-90-only Run tests only for Vim 9.0'
|
||||
echo ' --lua-only Run only Lua tests'
|
||||
echo ' --linters-only Run only Vint and custom checks'
|
||||
echo ' --fast Run only the fastest Vim and custom checks'
|
||||
echo ' --help Show this help text'
|
||||
@@ -147,6 +165,7 @@ if [ $# -ne 0 ]; then
|
||||
|
||||
# Don't run other tools when targeting tests.
|
||||
run_linters=0
|
||||
run_lua_tests=0
|
||||
fi
|
||||
|
||||
# Delete .swp files in the test directory, which cause Vim 8 to hang.
|
||||
@@ -250,6 +269,13 @@ for vim in $("$DOCKER" run --rm "$DOCKER_RUN_IMAGE" ls /vim-build/bin | grep '^n
|
||||
fi
|
||||
done
|
||||
|
||||
if ((run_lua_tests)); then
|
||||
echo "Starting Lua tests..."
|
||||
file_number=$((file_number+1))
|
||||
test/script/run-lua-tests $quiet_flag > "$output_dir/$file_number" 2>&1 &
|
||||
pid_list="$pid_list $!"
|
||||
fi
|
||||
|
||||
if ((run_linters)); then
|
||||
echo "Starting Vint..."
|
||||
file_number=$((file_number+1))
|
||||
|
||||
@@ -11,9 +11,30 @@
|
||||
"pending",
|
||||
"assert"
|
||||
],
|
||||
"workspace.checkThirdParty": false,
|
||||
"workspace.library": [
|
||||
"${3rd}/busted/library",
|
||||
"${env:HOME}/.luarocks/share/lua/5.4",
|
||||
"${env:HOME}/.luarocks/share/lua/5.3",
|
||||
"${env:HOME}/.luarocks/share/lua/5.2",
|
||||
"${env:HOME}/.luarocks/share/lua/5.1",
|
||||
"../../lua"
|
||||
],
|
||||
"runtime.pathStrict": true,
|
||||
"runtime.path": [
|
||||
"?.lua",
|
||||
"?/init.lua",
|
||||
"../../lua/?.lua",
|
||||
"../../lua/?/init.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.4/?.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.4/?/init.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.3/?.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.3/?/init.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.2/?.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.2/?/init.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.1/?.lua",
|
||||
"${env:HOME}/.luarocks/share/lua/5.1/?/init.lua"
|
||||
],
|
||||
"runtime.version": "LuaJIT",
|
||||
"hint.enable": false
|
||||
}
|
||||
|
||||
232
test/lua/ale_diagnostics_spec.lua
Normal file
232
test/lua/ale_diagnostics_spec.lua
Normal file
@@ -0,0 +1,232 @@
|
||||
local eq = assert.are.same
|
||||
local diagnostics
|
||||
|
||||
describe("ale.diagnostics.send", function()
|
||||
local buffer_map
|
||||
local signs_config
|
||||
local diagnostic_set_calls
|
||||
|
||||
setup(function()
|
||||
_G.vim = {
|
||||
api = {
|
||||
nvim_buf_get_var = function(buffer, key)
|
||||
local buffer_table = buffer_map[buffer] or {}
|
||||
local value = buffer_table[key]
|
||||
|
||||
if value == nil then
|
||||
error(key .. " is missing")
|
||||
end
|
||||
|
||||
return value
|
||||
end,
|
||||
nvim_create_namespace = function()
|
||||
return 42
|
||||
end,
|
||||
},
|
||||
diagnostic = {
|
||||
severity = {ERROR = 1, WARN = 2, INFO = 3},
|
||||
config = function()
|
||||
return {signs = signs_config}
|
||||
end,
|
||||
set = function(namespace, bufnr, _diagnostics, opts)
|
||||
table.insert(diagnostic_set_calls, {
|
||||
namespace = namespace,
|
||||
bufnr = bufnr,
|
||||
diagnostics = _diagnostics,
|
||||
opts = opts,
|
||||
})
|
||||
end,
|
||||
},
|
||||
tbl_extend = function(behavior, ...)
|
||||
assert(behavior == "force", "We should only use `force`")
|
||||
|
||||
local merged = {}
|
||||
|
||||
for _, arg in ipairs({...}) do
|
||||
for key, value in pairs(arg) do
|
||||
merged[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
return merged
|
||||
end,
|
||||
g = {},
|
||||
}
|
||||
|
||||
diagnostics = require("ale.diagnostics")
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
_G.vim = nil
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
buffer_map = {}
|
||||
diagnostic_set_calls = {}
|
||||
signs_config = false
|
||||
_G.vim.g = {}
|
||||
end)
|
||||
|
||||
it("should set an empty list of diagnostics correctly", function()
|
||||
diagnostics.send(7, {})
|
||||
|
||||
eq(
|
||||
{
|
||||
{
|
||||
namespace = 42,
|
||||
bufnr = 7,
|
||||
diagnostics = {},
|
||||
opts = {virtual_text = false}
|
||||
},
|
||||
},
|
||||
diagnostic_set_calls
|
||||
)
|
||||
end)
|
||||
|
||||
it("should handle basic case with all fields", function()
|
||||
diagnostics.send(1, {
|
||||
{
|
||||
bufnr = 1,
|
||||
lnum = 2,
|
||||
end_lnum = 3,
|
||||
col = 4,
|
||||
end_col = 5,
|
||||
type = "W",
|
||||
code = "123",
|
||||
text = "Warning message",
|
||||
linter_name = "eslint",
|
||||
},
|
||||
})
|
||||
eq({
|
||||
{
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 3,
|
||||
end_col = 5,
|
||||
severity = vim.diagnostic.severity.WARN,
|
||||
code = "123",
|
||||
message = "Warning message",
|
||||
source = "eslint",
|
||||
},
|
||||
}, diagnostic_set_calls[1].diagnostics)
|
||||
end)
|
||||
|
||||
it("should default end_lnum to lnum when missing", function()
|
||||
diagnostics.send(1, {
|
||||
{
|
||||
bufnr = 1,
|
||||
lnum = 5,
|
||||
col = 2,
|
||||
end_col = 8,
|
||||
type = "E",
|
||||
text = "Error message",
|
||||
linter_name = "mylinter",
|
||||
},
|
||||
})
|
||||
eq({
|
||||
{
|
||||
lnum = 4,
|
||||
end_lnum = 4,
|
||||
col = 1,
|
||||
end_col = 8,
|
||||
severity = vim.diagnostic.severity.ERROR,
|
||||
code = nil,
|
||||
message = "Error message",
|
||||
source = "mylinter",
|
||||
},
|
||||
}, diagnostic_set_calls[1].diagnostics)
|
||||
end)
|
||||
|
||||
it("should default col to 0 when missing", function()
|
||||
diagnostics.send(1, {
|
||||
{
|
||||
bufnr = 1,
|
||||
lnum = 10,
|
||||
end_lnum = 12,
|
||||
end_col = 6,
|
||||
type = "I",
|
||||
text = "Info message",
|
||||
},
|
||||
})
|
||||
eq({
|
||||
{
|
||||
lnum = 9,
|
||||
end_lnum = 11,
|
||||
col = 0,
|
||||
end_col = 6,
|
||||
severity = vim.diagnostic.severity.INFO,
|
||||
code = nil,
|
||||
message = "Info message",
|
||||
source = nil,
|
||||
},
|
||||
}, diagnostic_set_calls[1].diagnostics)
|
||||
end)
|
||||
|
||||
it("should ignore non-matching buffers", function()
|
||||
diagnostics.send(1, {
|
||||
{
|
||||
bufnr = 2,
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 1,
|
||||
end_col = 4,
|
||||
type = "W",
|
||||
text = "Message",
|
||||
},
|
||||
})
|
||||
eq({}, diagnostic_set_calls[1].diagnostics)
|
||||
end)
|
||||
|
||||
for _, set_signs_value in ipairs {1, true} do
|
||||
describe("signs with setting set_signs = " .. tostring(set_signs_value), function()
|
||||
before_each(function()
|
||||
_G.vim.g.ale_set_signs = set_signs_value
|
||||
_G.vim.g.ale_sign_priority = 10
|
||||
end)
|
||||
|
||||
it("and global config as `false` should enable signs with the given priority", function()
|
||||
diagnostics.send(7, {})
|
||||
eq({priority = 10}, diagnostic_set_calls[1].opts.signs)
|
||||
end)
|
||||
|
||||
it("and global config as a table should enable signs with the given priority", function()
|
||||
signs_config = {foo = "bar", priority = 5}
|
||||
diagnostics.send(7, {})
|
||||
eq(
|
||||
{foo = "bar", priority = 10},
|
||||
diagnostic_set_calls[1].opts.signs
|
||||
)
|
||||
end)
|
||||
|
||||
it("and global config as a function should enable signs with the given priority", function()
|
||||
signs_config = function()
|
||||
return {foo = "bar", priority = 5}
|
||||
end
|
||||
diagnostics.send(7, {})
|
||||
|
||||
local local_signs = diagnostic_set_calls[1].opts.signs
|
||||
|
||||
eq("function", type(local_signs))
|
||||
eq({foo = "bar", priority = 10}, local_signs())
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
it("should toggle virtual_text correctly", function()
|
||||
for _, value in ipairs({"all", "2", 2, "current", "1", 1, true}) do
|
||||
diagnostic_set_calls = {}
|
||||
_G.vim.g.ale_virtualtext_cursor = value
|
||||
diagnostics.send(7, {})
|
||||
|
||||
eq({virtual_text = true}, diagnostic_set_calls[1].opts)
|
||||
end
|
||||
|
||||
for _, value in ipairs({"disabled", "0", 0, false, nil}) do
|
||||
diagnostic_set_calls = {}
|
||||
_G.vim.g.ale_virtualtext_cursor = value
|
||||
diagnostics.send(7, {})
|
||||
|
||||
eq({virtual_text = false}, diagnostic_set_calls[1].opts)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
56
test/lua/ale_env_spec.lua
Normal file
56
test/lua/ale_env_spec.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
local eq = assert.are.same
|
||||
local ale = require("ale")
|
||||
|
||||
describe("ale.env", function()
|
||||
local is_win32 = false
|
||||
|
||||
setup(function()
|
||||
_G.vim = {
|
||||
o = setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
if key == "shell" then
|
||||
if is_win32 then
|
||||
return "cmd.exe"
|
||||
end
|
||||
|
||||
return "bash"
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
}),
|
||||
fn = {
|
||||
has = function(feature)
|
||||
return feature == "win32" and is_win32
|
||||
end,
|
||||
-- Mock a very poor version of shellescape() for Unix
|
||||
-- This shouldn't be called for Windows
|
||||
shellescape = function(str)
|
||||
return "'" .. str .. "'"
|
||||
end,
|
||||
fnamemodify = function(shell, _)
|
||||
return shell
|
||||
end
|
||||
}
|
||||
}
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
_G.vim = nil
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
is_win32 = false
|
||||
end)
|
||||
|
||||
it("should escape values correctly on Unix", function()
|
||||
eq("name='xxx' ", ale.env('name', 'xxx'))
|
||||
eq("name='foo bar' ", ale.env('name', 'foo bar'))
|
||||
end)
|
||||
|
||||
it("should escape values correctly on Windows", function()
|
||||
is_win32 = true
|
||||
eq('set name=xxx && ', ale.env('name', 'xxx'))
|
||||
eq('set "name=foo bar" && ', ale.env('name', 'foo bar'))
|
||||
end)
|
||||
end)
|
||||
224
test/lua/ale_lsp_spec.lua
Normal file
224
test/lua/ale_lsp_spec.lua
Normal file
@@ -0,0 +1,224 @@
|
||||
local eq = assert.are.same
|
||||
local lsp = require("ale.lsp")
|
||||
|
||||
describe("ale.lsp.start", function()
|
||||
local start_calls
|
||||
local rpc_connect_calls
|
||||
local vim_fn_calls
|
||||
local defer_calls
|
||||
|
||||
setup(function()
|
||||
_G.vim = {
|
||||
defer_fn = function(func, delay)
|
||||
table.insert(defer_calls, {func, delay})
|
||||
end,
|
||||
fn = setmetatable({}, {
|
||||
__index = function(_, key)
|
||||
return function(...)
|
||||
table.insert(vim_fn_calls, {key, ...})
|
||||
|
||||
if key == "ale#lsp#GetLanguage" then
|
||||
return "python"
|
||||
end
|
||||
|
||||
if key ~= "ale#lsp_linter#HandleLSPResponse"
|
||||
and key ~= "ale#lsp#UpdateCapabilities"
|
||||
and key ~= "ale#lsp#CallInitCallbacks"
|
||||
then
|
||||
assert(false, "Invalid ALE function: " .. key)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
}),
|
||||
lsp = {
|
||||
rpc = {
|
||||
connect = function(host, port)
|
||||
return function(dispatch)
|
||||
table.insert(rpc_connect_calls, {
|
||||
host = host,
|
||||
port = port,
|
||||
dispatch = dispatch,
|
||||
})
|
||||
end
|
||||
end,
|
||||
},
|
||||
start = function(...)
|
||||
table.insert(start_calls, {...})
|
||||
|
||||
return 42
|
||||
end,
|
||||
},
|
||||
}
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
_G.vim = nil
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
start_calls = {}
|
||||
rpc_connect_calls = {}
|
||||
vim_fn_calls = {}
|
||||
defer_calls = {}
|
||||
end)
|
||||
|
||||
it("should start lsp programs with the correct arguments", function()
|
||||
lsp.start({
|
||||
name = "server:/code",
|
||||
cmd = "server",
|
||||
root_dir = "/code",
|
||||
-- This Boolean value somehow ends up in Dictionaries from
|
||||
-- Vim for init_options, and we need to remove it.
|
||||
init_options = {[true] = 123},
|
||||
})
|
||||
|
||||
-- Remove arguments with functions we can't apply equality checks
|
||||
-- for easily.
|
||||
for _, args in pairs(start_calls) do
|
||||
args[1].handlers = nil
|
||||
args[1].on_init = nil
|
||||
args[1].get_language_id = nil
|
||||
end
|
||||
|
||||
eq({
|
||||
{
|
||||
{
|
||||
cmd = "server",
|
||||
name = "server:/code",
|
||||
root_dir = "/code",
|
||||
init_options = {},
|
||||
},
|
||||
{attach = false, silent = true}
|
||||
}
|
||||
}, start_calls)
|
||||
eq({}, vim_fn_calls)
|
||||
end)
|
||||
|
||||
it("should start lsp socket connections with the correct arguments", function()
|
||||
lsp.start({
|
||||
name = "localhost:1234:/code",
|
||||
host = "localhost",
|
||||
port = 1234,
|
||||
root_dir = "/code",
|
||||
init_options = {foo = "bar"},
|
||||
})
|
||||
|
||||
local cmd
|
||||
|
||||
-- Remove arguments with functions we can't apply equality checks
|
||||
-- for easily.
|
||||
for _, args in pairs(start_calls) do
|
||||
cmd = args[1].cmd
|
||||
args[1].cmd = nil
|
||||
args[1].handlers = nil
|
||||
args[1].on_init = nil
|
||||
args[1].get_language_id = nil
|
||||
end
|
||||
|
||||
eq({
|
||||
{
|
||||
{
|
||||
name = "localhost:1234:/code",
|
||||
root_dir = "/code",
|
||||
init_options = {foo = "bar"},
|
||||
},
|
||||
{attach = false, silent = true}
|
||||
}
|
||||
}, start_calls)
|
||||
|
||||
cmd("dispatch_value")
|
||||
|
||||
eq({
|
||||
{dispatch = "dispatch_value", host = "localhost", port = 1234},
|
||||
}, rpc_connect_calls)
|
||||
eq({}, vim_fn_calls)
|
||||
end)
|
||||
|
||||
it("should return the client_id value from vim.lsp.start", function()
|
||||
eq(42, lsp.start({}))
|
||||
end)
|
||||
|
||||
it("should implement get_language_id correctly", function()
|
||||
lsp.start({name = "server:/code"})
|
||||
|
||||
eq(1, #start_calls)
|
||||
eq("python", start_calls[1][1].get_language_id(347, "ftype"))
|
||||
eq({{"ale#lsp#GetLanguage", "server:/code", 347}}, vim_fn_calls)
|
||||
end)
|
||||
|
||||
it("should initialize clients with ALE correctly", function()
|
||||
lsp.start({name = "server:/code"})
|
||||
|
||||
eq(1, #start_calls)
|
||||
|
||||
start_calls[1][1].on_init({server_capabilities = {cap = 1}})
|
||||
|
||||
eq({
|
||||
{"ale#lsp#UpdateCapabilities", "server:/code", {cap = 1}},
|
||||
}, vim_fn_calls)
|
||||
eq(1, #defer_calls)
|
||||
eq(2, #defer_calls[1])
|
||||
eq("function", type(defer_calls[1][1]))
|
||||
eq(0, defer_calls[1][2])
|
||||
|
||||
defer_calls[1][1]()
|
||||
|
||||
eq({
|
||||
{"ale#lsp#UpdateCapabilities", "server:/code", {cap = 1}},
|
||||
{"ale#lsp#CallInitCallbacks", "server:/code"},
|
||||
}, vim_fn_calls)
|
||||
end)
|
||||
|
||||
it("should configure handlers correctly", function()
|
||||
lsp.start({name = "server:/code"})
|
||||
|
||||
eq(1, #start_calls)
|
||||
|
||||
local handlers = start_calls[1][1].handlers
|
||||
local handler_names = {}
|
||||
|
||||
-- get keys from handlers
|
||||
for key, _ in pairs(handlers) do
|
||||
-- add key to handler_names mapping
|
||||
handler_names[key] = true
|
||||
end
|
||||
|
||||
eq({["textDocument/publishDiagnostics"] = true}, handler_names)
|
||||
|
||||
handlers["textDocument/publishDiagnostics"](nil, {
|
||||
{
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 3,
|
||||
end_col = 5,
|
||||
severity = 1,
|
||||
code = "123",
|
||||
message = "Warning message",
|
||||
},
|
||||
})
|
||||
|
||||
eq({
|
||||
{
|
||||
"ale#lsp_linter#HandleLSPResponse",
|
||||
"server:/code",
|
||||
{
|
||||
jsonrpc = "2.0",
|
||||
method = "textDocument/publishDiagnostics",
|
||||
params = {
|
||||
{
|
||||
lnum = 1,
|
||||
end_lnum = 2,
|
||||
col = 3,
|
||||
end_col = 5,
|
||||
severity = 1,
|
||||
code = "123",
|
||||
message = "Warning message",
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}, vim_fn_calls)
|
||||
end)
|
||||
end)
|
||||
51
test/lua/ale_var_spec.lua
Normal file
51
test/lua/ale_var_spec.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
local eq = assert.are.same
|
||||
local ale = require("ale")
|
||||
|
||||
describe("ale.var", function()
|
||||
local buffer_map
|
||||
|
||||
setup(function()
|
||||
_G.vim = {
|
||||
api = {
|
||||
nvim_buf_get_var = function(buffer, key)
|
||||
local buffer_table = buffer_map[buffer] or {}
|
||||
local value = buffer_table[key]
|
||||
|
||||
if value == nil then
|
||||
error(key .. " is missing")
|
||||
end
|
||||
|
||||
return value
|
||||
end,
|
||||
},
|
||||
g = {
|
||||
},
|
||||
}
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
_G.vim = nil
|
||||
end)
|
||||
|
||||
before_each(function()
|
||||
buffer_map = {}
|
||||
_G.vim.g = {}
|
||||
end)
|
||||
|
||||
it("should return nil for undefined variables", function()
|
||||
eq(nil, ale.var(1, "foo"))
|
||||
end)
|
||||
|
||||
it("should return buffer-local values, if set", function()
|
||||
_G.vim.g.ale_foo = "global-value"
|
||||
buffer_map[1] = {ale_foo = "buffer-value"}
|
||||
|
||||
eq("buffer-value", ale.var(1, "foo"))
|
||||
end)
|
||||
|
||||
it("should return global values, if set", function()
|
||||
_G.vim.g.ale_foo = "global-value"
|
||||
|
||||
eq("global-value", ale.var(1, "foo"))
|
||||
end)
|
||||
end)
|
||||
61
test/lua/windows_escaping_spec.lua
Normal file
61
test/lua/windows_escaping_spec.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
local eq = assert.are.same
|
||||
local ale = require("ale")
|
||||
|
||||
describe("ale.escape for cmd.exe", function()
|
||||
setup(function()
|
||||
_G.vim = {
|
||||
o = {
|
||||
shell = "cmd.exe"
|
||||
},
|
||||
fn = {
|
||||
fnamemodify = function(shell, _)
|
||||
return shell
|
||||
end
|
||||
}
|
||||
}
|
||||
end)
|
||||
|
||||
teardown(function()
|
||||
_G.vim = nil
|
||||
end)
|
||||
|
||||
it("should allow not escape paths without special characters", function()
|
||||
eq("C:", ale.escape("C:"))
|
||||
eq("C:\\", ale.escape("C:\\"))
|
||||
eq("python", ale.escape("python"))
|
||||
eq("C:\\foo\\bar", ale.escape("C:\\foo\\bar"))
|
||||
eq("/bar/baz", ale.escape("/bar/baz"))
|
||||
eq("nul", ale.escape("nul"))
|
||||
eq("'foo'", ale.escape("'foo'"))
|
||||
end)
|
||||
|
||||
it("should escape Windows paths with spaces appropriately", function()
|
||||
eq('"C:\\foo bar\\baz"', ale.escape('C:\\foo bar\\baz'))
|
||||
eq('"^foo bar^"', ale.escape('^foo bar^'))
|
||||
eq('"&foo bar&"', ale.escape('&foo bar&'))
|
||||
eq('"|foo bar|"', ale.escape('|foo bar|'))
|
||||
eq('"<foo bar<"', ale.escape('<foo bar<'))
|
||||
eq('">foo bar>"', ale.escape('>foo bar>'))
|
||||
eq('"^foo bar^"', ale.escape('^foo bar^'))
|
||||
eq('"\'foo\' \'bar\'"', ale.escape('\'foo\' \'bar\''))
|
||||
end)
|
||||
|
||||
it("should use caret escapes on special characters", function()
|
||||
eq('^^foo^^', ale.escape('^foo^'))
|
||||
eq('^&foo^&', ale.escape('&foo&'))
|
||||
eq('^|foo^|', ale.escape('|foo|'))
|
||||
eq('^<foo^<', ale.escape('<foo<'))
|
||||
eq('^>foo^>', ale.escape('>foo>'))
|
||||
eq('^^foo^^', ale.escape('^foo^'))
|
||||
eq('\'foo\'^^\'bar\'', ale.escape('\'foo\'^\'bar\''))
|
||||
end)
|
||||
|
||||
it("should escape percent characters", function()
|
||||
eq('%%foo%%', ale.escape('%foo%'))
|
||||
eq('C:\foo%%\bar\baz%%', ale.escape('C:\foo%\bar\baz%'))
|
||||
eq('"C:\foo bar%%\baz%%"', ale.escape('C:\foo bar%\baz%'))
|
||||
eq('^^%%foo%%', ale.escape('^%foo%'))
|
||||
eq('"^%%foo%% %%bar%%"', ale.escape('^%foo% %bar%'))
|
||||
eq('"^%%foo%% %%bar%% """""', ale.escape('^%foo% %bar% ""'))
|
||||
end)
|
||||
end)
|
||||
57
test/script/run-lua-tests
Executable file
57
test/script/run-lua-tests
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
docker_flags=(--rm -v "$PWD:/testplugin" -v "$PWD/test:/home" -w /testplugin/test/lua "$DOCKER_RUN_IMAGE")
|
||||
|
||||
quiet=0
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
case $1 in
|
||||
-q)
|
||||
quiet=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-?*)
|
||||
echo "Invalid argument: $1" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
function filter-busted-output() {
|
||||
local hit_failure_line=0
|
||||
|
||||
while read -r; do
|
||||
if ((quiet)); then
|
||||
# If we're using the quiet flag, the filter out lines until we hit
|
||||
# the first line with "Failure" and then print the rest.
|
||||
if ((hit_failure_line)); then
|
||||
echo "$REPLY"
|
||||
elif [[ "$REPLY" = *'Failure'* ]]; then
|
||||
hit_failure_line=1
|
||||
echo "$REPLY"
|
||||
fi
|
||||
else
|
||||
echo "$REPLY"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
exit_code=0
|
||||
|
||||
set -o pipefail
|
||||
"$DOCKER" run -a stdout "${docker_flags[@]}" /usr/bin/busted-5.1 \
|
||||
-m '../../lua/?.lua;../../lua/?/init.lua' . \
|
||||
--output utfTerminal | filter-busted-output || exit_code=$?
|
||||
set +o pipefail
|
||||
|
||||
exit "$exit_code"
|
||||
Reference in New Issue
Block a user