2017-04-03 09:55:49 +00:00
|
|
|
|
|
|
|
-- Import
|
|
|
|
|
|
|
|
local LIBRARY
|
|
|
|
do
|
|
|
|
local thispath = ... and select('1', ...):match('.+%.') or ''
|
|
|
|
local was_loaded, library = pcall(require, thispath..'library')
|
|
|
|
LIBRARY = was_loaded and library or {}
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Constants
|
|
|
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Util
|
|
|
|
|
|
|
|
local function get_function_info (f)
|
|
|
|
-- NOTE: Works best in LuaJIT or Lua 5.2+
|
|
|
|
|
|
|
|
-- Regarding get-info:
|
|
|
|
-- * No need to includ 'f'. Function is already known
|
|
|
|
-- * No need to include 'L' (active lines) option. Ignored
|
|
|
|
-- * No need to include 'n' (name and namewhat). Won't work.
|
|
|
|
local info = debug.getinfo(f, 'Su')
|
|
|
|
info.params = {}
|
|
|
|
info.ups = {}
|
|
|
|
info.env = debug.getfenv and debug.getfenv(f)
|
|
|
|
info.builtin = info.source == '=[C]'
|
|
|
|
for i = 1, info.nparams or 0 do info.params[i] = debug.getlocal(f, i) end
|
|
|
|
if info.isvararg or not info.nparams then info.params[#info.params+1] = '...' end
|
|
|
|
-- Get upvalues
|
|
|
|
for i = 1, info.nups do
|
|
|
|
local k, v = debug.getupvalue(f, i)
|
|
|
|
if k == '_ENV' and not debug.getfenv then
|
|
|
|
info.env = v
|
|
|
|
else
|
|
|
|
info.ups[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if info.source:sub(1,1) == '=' then info.defined_how = 'C'
|
|
|
|
elseif info.source:sub(1,1) == '@' then info.defined_how = 'file'
|
|
|
|
else info.defined_how = 'string'
|
|
|
|
end
|
|
|
|
|
|
|
|
if info.builtin and LIBRARY[f] then
|
|
|
|
info.name = LIBRARY[f].name
|
|
|
|
info.params[1] = LIBRARY[f].para
|
|
|
|
info.doc = LIBRARY[f].docs
|
|
|
|
end
|
|
|
|
|
|
|
|
return info
|
|
|
|
end
|
|
|
|
|
|
|
|
local function get_line_index (str, line_nr)
|
|
|
|
local index = 0
|
|
|
|
for _ = 2, line_nr do
|
|
|
|
index = str:find('\n', index, true)
|
|
|
|
if not index then return #str end
|
|
|
|
index = index + 1
|
|
|
|
end
|
|
|
|
return index
|
|
|
|
end
|
|
|
|
|
|
|
|
local function get_full_function_str (str, start_line, end_line)
|
2017-04-03 11:49:18 +00:00
|
|
|
-- Will attempt to find a string which refer to a function starting on
|
|
|
|
-- line `start_line` and ending on line `end_line`.
|
2017-04-03 09:55:49 +00:00
|
|
|
local start_line_index = get_line_index(str, start_line)
|
|
|
|
local end_line_index = get_line_index(str, end_line + 1)
|
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
local function_body = str:sub(start_line_index, end_line_index):match('function%s*[a-zA-Z0-9_.]*%s*(.+)end')
|
|
|
|
return 'function '..function_body..'end'
|
|
|
|
end
|
|
|
|
|
|
|
|
local function get_function_str_from_file (filename, start_line, end_line)
|
|
|
|
local file = io.open(filename, 'r')
|
|
|
|
local str = file:read('*all')
|
|
|
|
file:close()
|
|
|
|
return get_full_function_str(str, start_line, end_line)
|
2017-04-03 09:55:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function width_of_strings_in_l (l, start_i, end_i)
|
|
|
|
-- FIXME: Copy of the one in pretty.lua
|
|
|
|
local width = 0
|
|
|
|
for i = start_i or 1, (end_i or #l) do
|
|
|
|
width = width + #l[i]
|
|
|
|
end
|
|
|
|
return width
|
|
|
|
end
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
local function format_function_with_closure (value, options, depth, l, format_value)
|
|
|
|
local info = get_function_info(value)
|
|
|
|
|
|
|
|
--assert(info.nups > 0)
|
|
|
|
local function_str = nil
|
|
|
|
if (info.defined_how == 'string') then
|
|
|
|
function_str = get_full_function_str(info.source, info.linedefined, info.lastlinedefined)
|
|
|
|
else
|
|
|
|
function_str = get_function_str_from_file(info.short_src, info.linedefined, info.lastlinedefined)
|
|
|
|
end
|
|
|
|
|
|
|
|
if info.nups > 0 then l[#l+1] = '(function () ' end
|
|
|
|
-- Upvalues
|
|
|
|
for k, v in pairs(info.ups) do
|
|
|
|
l[#l+1] = 'local '
|
|
|
|
l[#l+1] = tostring(k)
|
|
|
|
l[#l+1] = ' = '
|
|
|
|
format_value(v, options, depth + 1, l)
|
|
|
|
l[#l+1] = '; '
|
|
|
|
end
|
|
|
|
-- Return function
|
|
|
|
if info.nups > 0 then l[#l+1] = 'return ' end
|
|
|
|
l[#l+1] = function_str
|
|
|
|
--
|
|
|
|
if info.nups > 0 then l[#l+1] = ' end)()' end
|
|
|
|
end
|
|
|
|
|
2017-04-03 09:55:49 +00:00
|
|
|
return function (value, options, depth, l, format_value)
|
|
|
|
local info = get_function_info(value)
|
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
if options.include_closure then
|
|
|
|
return format_function_with_closure(value, options, depth, l, format_value)
|
|
|
|
end
|
|
|
|
|
2017-04-03 09:55:49 +00:00
|
|
|
if info.defined_how == 'string' then
|
|
|
|
-- Function was defined as a string.
|
|
|
|
l[#l+1] = get_full_function_str(info.source, info.linedefined, info.lastlinedefined)
|
|
|
|
return;
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Include function modifier, and alignment info.
|
|
|
|
l[#l+1] = info.builtin and 'builtin ' or ''
|
|
|
|
l[#l+1] = { #l[#l], 'func_mod'}
|
|
|
|
|
|
|
|
-- Build rest of function signature
|
|
|
|
l[#l+1] = 'function ('
|
|
|
|
local top_before = #l
|
|
|
|
for _, param in ipairs(info.params) do l[#l+1], l[#l+2] = param, ', ' end
|
|
|
|
if l[#l] == ', ' then l[#l] = nil end
|
|
|
|
l[#l+1] = ')'
|
|
|
|
l[#l+1] = { width_of_strings_in_l(l, top_before), 'func_def' }
|
|
|
|
|
|
|
|
-- Cleanup and finish
|
|
|
|
if not options.more_function_info or depth ~= 0 then
|
|
|
|
l[#l+1] = ' ... end'
|
2017-04-03 11:49:18 +00:00
|
|
|
return;
|
|
|
|
end
|
2017-04-03 09:55:49 +00:00
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
-- More info! --
|
2017-04-03 09:55:49 +00:00
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
-- Name
|
|
|
|
if info.name then
|
|
|
|
l[#l+1] = '\n'
|
|
|
|
l[#l+1] = options.indent
|
|
|
|
l[#l+1] = '-- '
|
|
|
|
l[#l+1] = info.name
|
|
|
|
end
|
2017-04-03 09:55:49 +00:00
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
-- Doc
|
|
|
|
if info.doc then
|
|
|
|
for doc_line in info.doc:gmatch('[^\n]+') do
|
2017-04-03 09:55:49 +00:00
|
|
|
l[#l+1] = '\n'
|
|
|
|
l[#l+1] = options.indent
|
|
|
|
l[#l+1] = '-- '
|
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
l[#l+1] = doc_line
|
2017-04-03 09:55:49 +00:00
|
|
|
end
|
2017-04-03 11:49:18 +00:00
|
|
|
end
|
2017-04-03 09:55:49 +00:00
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
-- source
|
|
|
|
if not info.builtin then
|
|
|
|
l[#l+1] = '\n'
|
|
|
|
l[#l+1] = options.indent
|
|
|
|
l[#l+1] = ('-- source_file: \'%s\' '):format(info.short_src)
|
|
|
|
if info.linedefined == info.lastlinedefined then
|
|
|
|
l[#l+1] = ('[Line: %i]'):format(info.linedefined)
|
|
|
|
else
|
|
|
|
l[#l+1] = ('[Lines: %i - %i]'):format(info.linedefined, info.lastlinedefined)
|
2017-04-03 09:55:49 +00:00
|
|
|
end
|
2017-04-03 11:49:18 +00:00
|
|
|
end
|
2017-04-03 09:55:49 +00:00
|
|
|
|
2017-04-03 11:49:18 +00:00
|
|
|
-- upvalues
|
|
|
|
if info.nups > 0 and not info.builtin then
|
|
|
|
l[#l+1] = '\n'
|
2017-04-03 09:55:49 +00:00
|
|
|
l[#l+1] = options.indent
|
2017-04-03 11:49:18 +00:00
|
|
|
l[#l+1] = '-- up_values: '
|
|
|
|
format_value(info.ups, options, depth + 1, l)
|
|
|
|
end
|
|
|
|
|
|
|
|
if options._all_function_info then
|
|
|
|
-- NOTE: This is for testing/debugging/experimentation purposes.
|
|
|
|
|
|
|
|
local function_str = get_function_str_from_file(info.short_src, info.linedefined, info.lastlinedefined)
|
|
|
|
|
|
|
|
l[#l+1] = '\n\t--[[ Function Body\n\t'
|
|
|
|
l[#l+1] = function_str
|
|
|
|
l[#l+1] = '\n\t--]]'
|
|
|
|
|
|
|
|
l[#l+1] = '\n\t--[[\n\tNative repr:'
|
|
|
|
l[#l+1] = tostring(value)
|
|
|
|
l[#l+1] = '\n\t'
|
|
|
|
format_value(info, options, depth + 1, l)
|
|
|
|
l[#l+1] = '--]]'
|
2017-04-03 09:55:49 +00:00
|
|
|
end
|
2017-04-03 11:49:18 +00:00
|
|
|
|
|
|
|
l[#l+1] = '\n\n'
|
|
|
|
l[#l+1] = options.indent
|
|
|
|
l[#l+1] = '...\nend'
|
2017-04-03 09:55:49 +00:00
|
|
|
end
|