mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-24 21:11:28 +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:
@@ -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)
|
||||
Reference in New Issue
Block a user