mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 05:20:04 +08:00
217 lines
5.5 KiB
Lua
Vendored
217 lines
5.5 KiB
Lua
Vendored
-- module will not return anything, only register formatters with the main assert engine
|
|
local assert = require('luassert.assert')
|
|
|
|
local colors = setmetatable({
|
|
none = function(c) return c end
|
|
},{ __index = function(self, key)
|
|
local ok, term = pcall(require, 'term')
|
|
local isatty = io.type(io.stdout) == 'file' and ok and term.isatty(io.stdout)
|
|
if not ok or not isatty or not term.colors then
|
|
return function(c) return c end
|
|
end
|
|
return function(c)
|
|
for token in key:gmatch("[^%.]+") do
|
|
c = term.colors[token](c)
|
|
end
|
|
return c
|
|
end
|
|
end
|
|
})
|
|
|
|
local function fmt_string(arg)
|
|
if type(arg) == "string" then
|
|
return string.format("(string) '%s'", arg)
|
|
end
|
|
end
|
|
|
|
-- A version of tostring which formats numbers more precisely.
|
|
local function tostr(arg)
|
|
if type(arg) ~= "number" then
|
|
return tostring(arg)
|
|
end
|
|
|
|
if arg ~= arg then
|
|
return "NaN"
|
|
elseif arg == 1/0 then
|
|
return "Inf"
|
|
elseif arg == -1/0 then
|
|
return "-Inf"
|
|
end
|
|
|
|
local str = string.format("%.20g", arg)
|
|
|
|
if math.type and math.type(arg) == "float" and not str:find("[%.,]") then
|
|
-- Number is a float but looks like an integer.
|
|
-- Insert ".0" after first run of digits.
|
|
str = str:gsub("%d+", "%0.0", 1)
|
|
end
|
|
|
|
return str
|
|
end
|
|
|
|
local function fmt_number(arg)
|
|
if type(arg) == "number" then
|
|
return string.format("(number) %s", tostr(arg))
|
|
end
|
|
end
|
|
|
|
local function fmt_boolean(arg)
|
|
if type(arg) == "boolean" then
|
|
return string.format("(boolean) %s", tostring(arg))
|
|
end
|
|
end
|
|
|
|
local function fmt_nil(arg)
|
|
if type(arg) == "nil" then
|
|
return "(nil)"
|
|
end
|
|
end
|
|
|
|
local type_priorities = {
|
|
number = 1,
|
|
boolean = 2,
|
|
string = 3,
|
|
table = 4,
|
|
["function"] = 5,
|
|
userdata = 6,
|
|
thread = 7
|
|
}
|
|
|
|
local function is_in_array_part(key, length)
|
|
return type(key) == "number" and 1 <= key and key <= length and math.floor(key) == key
|
|
end
|
|
|
|
local function get_sorted_keys(t)
|
|
local keys = {}
|
|
local nkeys = 0
|
|
|
|
for key in pairs(t) do
|
|
nkeys = nkeys + 1
|
|
keys[nkeys] = key
|
|
end
|
|
|
|
local length = #t
|
|
|
|
local function key_comparator(key1, key2)
|
|
local type1, type2 = type(key1), type(key2)
|
|
local priority1 = is_in_array_part(key1, length) and 0 or type_priorities[type1] or 8
|
|
local priority2 = is_in_array_part(key2, length) and 0 or type_priorities[type2] or 8
|
|
|
|
if priority1 == priority2 then
|
|
if type1 == "string" or type1 == "number" then
|
|
return key1 < key2
|
|
elseif type1 == "boolean" then
|
|
return key1 -- put true before false
|
|
end
|
|
else
|
|
return priority1 < priority2
|
|
end
|
|
end
|
|
|
|
table.sort(keys, key_comparator)
|
|
return keys, nkeys
|
|
end
|
|
|
|
local function fmt_table(arg, fmtargs)
|
|
if type(arg) ~= "table" then
|
|
return
|
|
end
|
|
|
|
local tmax = assert:get_parameter("TableFormatLevel")
|
|
local showrec = assert:get_parameter("TableFormatShowRecursion")
|
|
local errchar = assert:get_parameter("TableErrorHighlightCharacter") or ""
|
|
local errcolor = assert:get_parameter("TableErrorHighlightColor") or "none"
|
|
local crumbs = fmtargs and fmtargs.crumbs or {}
|
|
local cache = {}
|
|
local type_desc
|
|
|
|
if getmetatable(arg) == nil then
|
|
type_desc = "(" .. tostring(arg) .. ") "
|
|
elseif not pcall(setmetatable, arg, getmetatable(arg)) then
|
|
-- cannot set same metatable, so it is protected, skip id
|
|
type_desc = "(table) "
|
|
else
|
|
-- unprotected metatable, temporary remove the mt
|
|
local mt = getmetatable(arg)
|
|
setmetatable(arg, nil)
|
|
type_desc = "(" .. tostring(arg) .. ") "
|
|
setmetatable(arg, mt)
|
|
end
|
|
|
|
local function ft(t, l, with_crumbs)
|
|
if showrec and cache[t] and cache[t] > 0 then
|
|
return "{ ... recursive }"
|
|
end
|
|
|
|
if next(t) == nil then
|
|
return "{ }"
|
|
end
|
|
|
|
if l > tmax and tmax >= 0 then
|
|
return "{ ... more }"
|
|
end
|
|
|
|
local result = "{"
|
|
local keys, nkeys = get_sorted_keys(t)
|
|
|
|
cache[t] = (cache[t] or 0) + 1
|
|
local crumb = crumbs[#crumbs - l + 1]
|
|
|
|
for i = 1, nkeys do
|
|
local k = keys[i]
|
|
local v = t[k]
|
|
local use_crumbs = with_crumbs and k == crumb
|
|
|
|
if type(v) == "table" then
|
|
v = ft(v, l + 1, use_crumbs)
|
|
elseif type(v) == "string" then
|
|
v = "'"..v.."'"
|
|
end
|
|
|
|
local ch = use_crumbs and errchar or ""
|
|
local indent = string.rep(" ",l * 2 - ch:len())
|
|
local mark = (ch:len() == 0 and "" or colors[errcolor](ch))
|
|
result = result .. string.format("\n%s%s[%s] = %s", indent, mark, tostr(k), tostr(v))
|
|
end
|
|
|
|
cache[t] = cache[t] - 1
|
|
|
|
return result .. " }"
|
|
end
|
|
|
|
return type_desc .. ft(arg, 1, true)
|
|
end
|
|
|
|
local function fmt_function(arg)
|
|
if type(arg) == "function" then
|
|
local debug_info = debug.getinfo(arg)
|
|
return string.format("%s @ line %s in %s", tostring(arg), tostring(debug_info.linedefined), tostring(debug_info.source))
|
|
end
|
|
end
|
|
|
|
local function fmt_userdata(arg)
|
|
if type(arg) == "userdata" then
|
|
return string.format("(userdata) '%s'", tostring(arg))
|
|
end
|
|
end
|
|
|
|
local function fmt_thread(arg)
|
|
if type(arg) == "thread" then
|
|
return string.format("(thread) '%s'", tostring(arg))
|
|
end
|
|
end
|
|
|
|
assert:add_formatter(fmt_string)
|
|
assert:add_formatter(fmt_number)
|
|
assert:add_formatter(fmt_boolean)
|
|
assert:add_formatter(fmt_nil)
|
|
assert:add_formatter(fmt_table)
|
|
assert:add_formatter(fmt_function)
|
|
assert:add_formatter(fmt_userdata)
|
|
assert:add_formatter(fmt_thread)
|
|
-- Set default table display depth for table formatter
|
|
assert:set_parameter("TableFormatLevel", 3)
|
|
assert:set_parameter("TableFormatShowRecursion", false)
|
|
assert:set_parameter("TableErrorHighlightCharacter", "*")
|
|
assert:set_parameter("TableErrorHighlightColor", "none")
|