Improved alignment of functions.
This commit is contained in:
parent
1bfd4abb96
commit
2cc9301c58
10
README.md
10
README.md
|
@ -26,9 +26,12 @@ Contrast the two following pieces of code:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This project came out of the frustration with existing pretty printers, which
|
This project is the outcome of my frustration with existing pretty printers, and
|
||||||
would often employ simpler heuristics, each good at displaying some specific
|
a desire to expand upon the pretty printer I developed for
|
||||||
structure, but bad at others. See below for other pretty printers.
|
[Xenoterm](https://gitfub.space/takunomi/Xenoterm). The default Xenoterm pretty
|
||||||
|
printer is much simpler than `pretty`, but the enhancements compared to other
|
||||||
|
pretty printers, inspired me to create `pretty`. See the bottom of the page for
|
||||||
|
other pretty printers.
|
||||||
|
|
||||||
Another aspect where `pretty` shines is in exploratory programming, when
|
Another aspect where `pretty` shines is in exploratory programming, when
|
||||||
attempting to avoid reliance on outside documentation.
|
attempting to avoid reliance on outside documentation.
|
||||||
|
@ -77,6 +80,7 @@ I'm looking into implementing following features:
|
||||||
non-breaking strings. Maybe even attempt to break near whitespace.
|
non-breaking strings. Maybe even attempt to break near whitespace.
|
||||||
- Attempt to fit output within a predefined width limit. Default to 80(?)
|
- Attempt to fit output within a predefined width limit. Default to 80(?)
|
||||||
- Find a better name than `pretty`.
|
- Find a better name than `pretty`.
|
||||||
|
- Add options for colored output, maybe even support multiple?
|
||||||
|
|
||||||
## Other pretty printers
|
## Other pretty printers
|
||||||
|
|
||||||
|
|
62
function.lua
62
function.lua
|
@ -85,7 +85,8 @@ local function get_function_info (f)
|
||||||
end
|
end
|
||||||
|
|
||||||
if info.source:sub(1,1) == '=' then info.defined_how = 'C'
|
if info.source:sub(1,1) == '=' then info.defined_how = 'C'
|
||||||
elseif info.source:sub(1,1) == '@' then info.defined_how = 'file'
|
elseif info.source:sub(1,1) == '@' then info.defined_how = 'file'
|
||||||
|
elseif info.source:find'^%w+.lua$' then info.defined_how = 'file' -- Hotfix for Love2d boot.lua issue.
|
||||||
else info.defined_how = 'string'
|
else info.defined_how = 'string'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -99,6 +100,9 @@ local function get_function_info (f)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_line_index (str, line_nr)
|
local function get_line_index (str, line_nr)
|
||||||
|
assert(type(str) == 'string')
|
||||||
|
assert(type(line_nr) == 'number')
|
||||||
|
|
||||||
local index = 0
|
local index = 0
|
||||||
for _ = 2, line_nr do
|
for _ = 2, line_nr do
|
||||||
index = str:find('\n', index, true)
|
index = str:find('\n', index, true)
|
||||||
|
@ -108,23 +112,40 @@ local function get_line_index (str, line_nr)
|
||||||
return index
|
return index
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_full_function_str (str, start_line, end_line)
|
local function get_function_paramlist_and_body (str, start_line, end_line)
|
||||||
-- Will attempt to find a string which refer to a function starting on
|
-- Will attempt to find a string which refer to a function starting on
|
||||||
-- line `start_line` and ending on line `end_line`.
|
-- line `start_line` and ending on line `end_line`.
|
||||||
|
|
||||||
|
assert(type(str) == 'string')
|
||||||
|
assert(type(start_line) == 'number')
|
||||||
|
assert(type(end_line) == 'number')
|
||||||
|
|
||||||
local start_line_index = get_line_index(str, start_line)
|
local start_line_index = get_line_index(str, start_line)
|
||||||
local end_line_index = get_line_index(str, end_line + 1)
|
local end_line_index = get_line_index(str, end_line + 1)
|
||||||
|
|
||||||
local function_body = str:sub(start_line_index, end_line_index):match('function%s*[a-zA-Z0-9_.]*%s*(.+)end')
|
local function_params, function_body = str:sub(start_line_index, end_line_index):match('function%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.-)[ \t]*end')
|
||||||
return 'function '..function_body..'end'
|
--print(function_params, function_body)
|
||||||
|
assert(type(function_params) == 'string' and type(function_body) == 'string')
|
||||||
|
return function_params, function_body
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_full_function_str (...)
|
||||||
|
local function_params, function_body = get_function_paramlist_and_body(...)
|
||||||
|
return 'function '..function_params..' '..function_body..' end'
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_function_str_from_file (filename, start_line, end_line)
|
local function get_function_str_from_file (filename, start_line, end_line)
|
||||||
|
assert(type(filename) == 'string')
|
||||||
|
assert(type(start_line) == 'number')
|
||||||
|
assert(type(end_line) == 'number')
|
||||||
|
|
||||||
local file = io.open(filename, 'r')
|
local file = io.open(filename, 'r')
|
||||||
local str = file:read('*all')
|
local str = file:read('*all')
|
||||||
file:close()
|
file:close()
|
||||||
return get_full_function_str(str, start_line, end_line)
|
return get_full_function_str(str, start_line, end_line)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function width_of_strings_in_l (l, start_i, end_i)
|
local function width_of_strings_in_l (l, start_i, end_i)
|
||||||
-- FIXME: Copy of the one in pretty.lua
|
-- FIXME: Copy of the one in pretty.lua
|
||||||
local width = 0
|
local width = 0
|
||||||
|
@ -187,15 +208,15 @@ return function (value, depth, l, format_value)
|
||||||
return format_function_with_closure(value, depth, l, format_value)
|
return format_function_with_closure(value, depth, l, format_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
if info.defined_how == 'string' then
|
local function_params, function_body = nil, '...'
|
||||||
|
|
||||||
|
if info.defined_how == 'string' and l.options.embed_loaded_funcs then
|
||||||
-- Function was defined as a string.
|
-- Function was defined as a string.
|
||||||
l[#l+1] = get_full_function_str(info.source, info.linedefined, info.lastlinedefined)
|
function_params, function_body = get_function_paramlist_and_body(info.source, info.linedefined, info.lastlinedefined)
|
||||||
return;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if info.builtin and l.options.short_builtins then
|
if info.builtin and l.options.short_builtins then
|
||||||
l[#l+1] = info.name
|
return l(info.name);
|
||||||
return;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Include function modifier, and alignment info.
|
-- Include function modifier, and alignment info.
|
||||||
|
@ -203,17 +224,26 @@ return function (value, depth, l, format_value)
|
||||||
l[#l+1] = { 'align', 'func_mod', #l[#l]}
|
l[#l+1] = { 'align', 'func_mod', #l[#l]}
|
||||||
|
|
||||||
-- Build rest of function signature
|
-- Build rest of function signature
|
||||||
l[#l+1] = 'function ('
|
l[#l+1] = 'function '
|
||||||
local top_before = #l
|
local top_before = #l
|
||||||
for _, param in ipairs(info.params) do l[#l+1], l[#l+2] = param, ', ' end
|
if function_params then
|
||||||
if l[#l] == ', ' then l[#l] = nil end
|
l[#l+1] = function_params
|
||||||
l[#l+1] = ')'
|
else
|
||||||
|
l[#l+1] = '('
|
||||||
|
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] = ')'
|
||||||
|
end
|
||||||
l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) }
|
l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) }
|
||||||
|
|
||||||
-- Cleanup and finish
|
-- Cleanup and finish
|
||||||
|
print(not l.options.more_function_info, depth ~= 0)
|
||||||
if not l.options.more_function_info or depth ~= 0 then
|
if not l.options.more_function_info or depth ~= 0 then
|
||||||
l[#l+1] = ' ... end'
|
l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' '
|
||||||
return;
|
l[#l+1] = function_body
|
||||||
|
l[#l+1] = { 'align', 'func_end', #function_body }
|
||||||
|
l[#l+1] = (function_body:sub(-1) == '\n' or function_body == '') and '' or ' '
|
||||||
|
return l 'end'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- More info! --
|
-- More info! --
|
||||||
|
|
12
pretty.lua
12
pretty.lua
|
@ -249,7 +249,8 @@ local function fix_seperator_info (l, indent_char, max_depth)
|
||||||
if type(l[i]) ~= 'table' then
|
if type(l[i]) ~= 'table' then
|
||||||
-- Do nothing
|
-- Do nothing
|
||||||
elseif l[i][1] == 'seperator' then
|
elseif l[i][1] == 'seperator' then
|
||||||
l[i] = inline_depth and ' ' or ('\n' .. indent_char:rep(depth))
|
assert(l[i][2] == nil or type(l[i][2]) == 'string')
|
||||||
|
l[i] = (l[i][2] or '') .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth)))
|
||||||
elseif l[i][1] == 'indent' then
|
elseif l[i][1] == 'indent' then
|
||||||
depth, inline_depth = depth + 1, inline_depth or l[i][3] == 'inline' and depth + 1 or nil
|
depth, inline_depth = depth + 1, inline_depth or l[i][3] == 'inline' and depth + 1 or nil
|
||||||
l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth)))
|
l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth)))
|
||||||
|
@ -340,12 +341,14 @@ function format_table (t, depth, l)
|
||||||
if l.options._table_addr_comment then l[#l+1], l[#l+2] = '--[['..table_info.address..']]', {'seperator'} end
|
if l.options._table_addr_comment then l[#l+1], l[#l+2] = '--[['..table_info.address..']]', {'seperator'} end
|
||||||
for _, pair in ipairs(key_value_pairs) do
|
for _, pair in ipairs(key_value_pairs) do
|
||||||
pair_format_func(l, pair[1], pair[2], depth + 1)
|
pair_format_func(l, pair[1], pair[2], depth + 1)
|
||||||
l[#l+1] = ','
|
l[#l+1] = {'seperator', ','}
|
||||||
l[#l+1] = {'seperator'}
|
|
||||||
end
|
end
|
||||||
if l[#l][1] == 'seperator' then l[#l-1], l[#l] = nil, nil end
|
if l[#l][1] == 'seperator' then l[#l] = nil end
|
||||||
l[#l+1] = {'unindent', '}'}
|
l[#l+1] = {'unindent', '}'}
|
||||||
|
|
||||||
|
--require 'fun' () -- DEBUG!
|
||||||
|
--DEBUG = map(operator.identity, l) -- DEBUG!
|
||||||
|
|
||||||
-- Decide for short or long table formatting.
|
-- Decide for short or long table formatting.
|
||||||
local table_width = width_of_strings_in_l(l, start_of_table_i)
|
local table_width = width_of_strings_in_l(l, start_of_table_i)
|
||||||
if table_width <= MAX_WIDTH_FOR_SINGLE_LINE_TABLE then
|
if table_width <= MAX_WIDTH_FOR_SINGLE_LINE_TABLE then
|
||||||
|
@ -471,6 +474,7 @@ local KNOWN_OPTIONS = {
|
||||||
math_shorthand = { type = 'boolean', default = false },
|
math_shorthand = { type = 'boolean', default = false },
|
||||||
soft_numbers = { type = 'boolean', default = true }, -- TODO: Add support for maximally precise numbers.
|
soft_numbers = { type = 'boolean', default = true }, -- TODO: Add support for maximally precise numbers.
|
||||||
max_depth = { type = 'number', default = math.huge },
|
max_depth = { type = 'number', default = math.huge },
|
||||||
|
embed_loaded_funcs = { type = 'boolean', default = false },
|
||||||
more_function_info = { type = 'boolean', default = false },
|
more_function_info = { type = 'boolean', default = false },
|
||||||
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} },
|
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} },
|
||||||
short_builtins = { type = 'boolean', default = false },
|
short_builtins = { type = 'boolean', default = false },
|
||||||
|
|
|
@ -135,18 +135,21 @@ end
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
input = loadstring('return function () end')(),
|
input = loadstring('return function () end')(),
|
||||||
expect = 'function () end',
|
expect = 'function () end',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
input = loadstring('return function () return function () end end')(),
|
input = loadstring('return function () return function () end end')(),
|
||||||
expect = 'function () return function () end end',
|
expect = 'function () return function () end end',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
input = loadstring('return function () return function () end\nend')()(),
|
input = loadstring('return function () return function () end\nend')()(),
|
||||||
expect = 'function () end',
|
expect = 'function () end',
|
||||||
}
|
}
|
||||||
|
@ -154,6 +157,7 @@ format_test {
|
||||||
format_test {
|
format_test {
|
||||||
-- NOTE: This is HARD to fix. It's thus longerterm
|
-- NOTE: This is HARD to fix. It's thus longerterm
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
input = loadstring('return function () return function () end end')()(),
|
input = loadstring('return function () return function () end end')()(),
|
||||||
expect = 'function () end',
|
expect = 'function () end',
|
||||||
}
|
}
|
||||||
|
@ -161,14 +165,14 @@ format_test {
|
||||||
format_test {
|
format_test {
|
||||||
-- More function info allows one to even get the function whole, if it was defined in a string.
|
-- More function info allows one to even get the function whole, if it was defined in a string.
|
||||||
input = loadstring('return function (a, b) return a + b end')(),
|
input = loadstring('return function (a, b) return a + b end')(),
|
||||||
options = { more_function_info = true },
|
options = { embed_loaded_funcs = true },
|
||||||
expect = 'function (a, b) return a + b end',
|
expect = 'function (a, b) return a + b end',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
-- More function info allows one to even get the function whole, if it was defined in a string.
|
-- More function info allows one to even get the function whole, if it was defined in a string.
|
||||||
input = loadstring('return function (a, b)\n return a + b\nend')(),
|
input = loadstring('return function (a, b)\n return a + b\nend')(),
|
||||||
options = { more_function_info = true },
|
options = { embed_loaded_funcs = true },
|
||||||
expect = 'function (a, b)\n return a + b\nend',
|
expect = 'function (a, b)\n return a + b\nend',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,6 +366,22 @@ format_test {
|
||||||
expect = '{\n abs = function (x) ... end,\n random = builtin function ([m [, n]) ... end\n}',
|
expect = '{\n abs = function (x) ... end,\n random = builtin function ([m [, n]) ... end\n}',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
-- The function part should align, even if one is loaded from a string.
|
||||||
|
adv_getlocal = true,
|
||||||
|
input = { random = math.random, abs = loadstring('return function () return 1 end')() },
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
|
expect = '{\n abs = function () return 1 end,\n random = builtin function ([m [, n]) ... end\n}',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
-- The end part should align when both are loaded from strings.
|
||||||
|
adv_getlocal = true,
|
||||||
|
input = { a = loadstring'return function(a) return a end'(), b = loadstring'return function (...) return ... end'() },
|
||||||
|
options = { embed_loaded_funcs = true },
|
||||||
|
expect = '{\n a = function (a) return a end,\n b = function (...) return ... end\n}',
|
||||||
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
-- No special indent if no special function modifier.
|
-- No special indent if no special function modifier.
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user