diff options
Diffstat (limited to 'error_handling.lua')
| -rw-r--r-- | error_handling.lua | 103 |
1 files changed, 99 insertions, 4 deletions
diff --git a/error_handling.lua b/error_handling.lua index b7d5d13..ecf6088 100644 --- a/error_handling.lua +++ b/error_handling.lua @@ -57,14 +57,109 @@ debug.getinfo = function(thread, f, what) end return info end +local strdist +strdist = function(a, b, cache) + if cache == nil then + cache = { } + end + if a == b then + return 0 + end + if #a < #b then + a, b = b, a + end + if b == "" then + return #a + end + local k = a .. '\003' .. b + if not (cache[k]) then + cache[k] = math.min(strdist(a:sub(1, -2), b, cache) + 1, strdist(a, b:sub(1, -2), cache) + 1, strdist(a:sub(1, -2), b:sub(1, -2), cache) + (a:sub(-1) ~= b:sub(-1) and 1.1 or 0)) + if #a >= 2 and #b >= 2 and a:sub(-1, -1) == b:sub(-2, -2) and a:sub(-2, -2) == b:sub(-1, -1) then + cache[k] = math.min(cache[k], strdist(a:sub(1, -3), b:sub(1, -3), cache) + 1) + end + end + return cache[k] +end local enhance_error -enhance_error = function(error_message, start_fn, stop_fn) +enhance_error = function(error_message) if not (error_message and error_message:match("%d|")) then error_message = error_message or "" do - local fn = (error_message:match("attempt to call a nil value %(global '(.*)'%)") or error_message:match("attempt to call global '(.*)' %(a nil value%)")) - if fn then - error_message = "The action '" .. tostring(fn:from_lua_id()) .. "' is not defined." + local fn_name = (error_message:match("attempt to call a nil value %(global '(.*)'%)") or error_message:match("attempt to call global '(.*)' %(a nil value%)")) + if fn_name then + local action_name = fn_name:from_lua_id() + error_message = "The action '" .. tostring(action_name) .. "' is not defined." + local func = debug.getinfo(2, 'f').func + local ename, env = debug.getupvalue(func, 1) + if not (ename == "_ENV" or ename == "_G") then + func = debug.getinfo(3, 'f').func + ename, env = debug.getupvalue(func, 1) + end + local THRESHOLD = math.min(4.5, .9 * #action_name) + local candidates = { } + local cache = { } + for i = 1, 99 do + local k, v = debug.getlocal(2, i) + if k == nil then + break + end + if not (k:sub(1, 1) == "(" or type(v) ~= 'function') then + k = k:from_lua_id() + if strdist(k, action_name, cache) <= THRESHOLD and k ~= "" then + table.insert(candidates, k) + end + end + end + for i = 1, debug.getinfo(func, 'u').nups do + local k, v = debug.getupvalue(func, i) + if not (k:sub(1, 1) == "(" or type(v) ~= 'function') then + k = k:from_lua_id() + if strdist(k, action_name, cache) <= THRESHOLD and k ~= "" then + table.insert(candidates, k) + end + end + end + local scan + scan = function(t, is_lua_id) + for k, v in pairs(t) do + if type(k) == 'string' and type(v) == 'function' then + if not (is_lua_id) then + k = k:from_lua_id() + end + if strdist(k, action_name, cache) <= THRESHOLD and k ~= "" then + table.insert(candidates, k) + end + end + end + end + scan(env.COMPILE_RULES, true) + scan(env.COMPILE_RULES._IMPORTS, true) + scan(env) + scan(env._IMPORTS) + if #candidates > 0 then + for _index_0 = 1, #candidates do + local c = candidates[_index_0] + THRESHOLD = math.min(THRESHOLD, strdist(c, action_name, cache)) + end + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #candidates do + local c = candidates[_index_0] + if strdist(c, action_name, cache) <= THRESHOLD then + _accum_0[_len_0] = c + _len_0 = _len_0 + 1 + end + end + candidates = _accum_0 + end + if #candidates == 1 then + error_message = error_message .. "\n\x1b[3mSuggestion: Maybe you meant '" .. tostring(candidates[1]) .. "'? " + elseif #candidates > 0 then + local last = table.remove(candidates) + error_message = error_message .. ("\n" .. C('italic', "Suggestion: Maybe you meant '" .. tostring(table.concat(candidates, "', '")) .. "'" .. tostring(#candidates > 1 and ',' or '') .. " or '" .. tostring(last) .. "'? ")) + end + end end end local level = 2 |
