1
0

Improved alignment of functions.

This commit is contained in:
Jon Michael Aanes 2017-06-11 13:53:06 +02:00
parent 1bfd4abb96
commit 2cc9301c58
4 changed files with 83 additions and 25 deletions

View File

@ -26,9 +26,12 @@ Contrast the two following pieces of code:
}
```
This project came out of the frustration with existing pretty printers, which
would often employ simpler heuristics, each good at displaying some specific
structure, but bad at others. See below for other pretty printers.
This project is the outcome of my frustration with existing pretty printers, and
a desire to expand upon the pretty printer I developed for
[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
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.
- Attempt to fit output within a predefined width limit. Default to 80(?)
- Find a better name than `pretty`.
- Add options for colored output, maybe even support multiple?
## Other pretty printers

View File

@ -85,7 +85,8 @@ local function get_function_info (f)
end
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'
end
@ -99,6 +100,9 @@ local function get_function_info (f)
end
local function get_line_index (str, line_nr)
assert(type(str) == 'string')
assert(type(line_nr) == 'number')
local index = 0
for _ = 2, line_nr do
index = str:find('\n', index, true)
@ -108,23 +112,40 @@ local function get_line_index (str, line_nr)
return index
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
-- 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 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')
return 'function '..function_body..'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')
--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
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 str = file:read('*all')
file:close()
return get_full_function_str(str, start_line, end_line)
end
local function width_of_strings_in_l (l, start_i, end_i)
-- FIXME: Copy of the one in pretty.lua
local width = 0
@ -187,15 +208,15 @@ return function (value, depth, l, format_value)
return format_function_with_closure(value, depth, l, format_value)
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.
l[#l+1] = get_full_function_str(info.source, info.linedefined, info.lastlinedefined)
return;
function_params, function_body = get_function_paramlist_and_body(info.source, info.linedefined, info.lastlinedefined)
end
if info.builtin and l.options.short_builtins then
l[#l+1] = info.name
return;
return l(info.name);
end
-- 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]}
-- 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] = 'function '
local top_before = #l
if function_params then
l[#l+1] = function_params
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) }
-- Cleanup and finish
print(not l.options.more_function_info, depth ~= 0)
if not l.options.more_function_info or depth ~= 0 then
l[#l+1] = ' ... end'
return;
l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' '
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
-- More info! --

View File

@ -249,7 +249,8 @@ local function fix_seperator_info (l, indent_char, max_depth)
if type(l[i]) ~= 'table' then
-- Do nothing
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
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)))
@ -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
for _, pair in ipairs(key_value_pairs) do
pair_format_func(l, pair[1], pair[2], depth + 1)
l[#l+1] = ','
l[#l+1] = {'seperator'}
l[#l+1] = {'seperator', ','}
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', '}'}
--require 'fun' () -- DEBUG!
--DEBUG = map(operator.identity, l) -- DEBUG!
-- Decide for short or long table formatting.
local table_width = width_of_strings_in_l(l, start_of_table_i)
if table_width <= MAX_WIDTH_FOR_SINGLE_LINE_TABLE then
@ -471,6 +474,7 @@ local KNOWN_OPTIONS = {
math_shorthand = { type = 'boolean', default = false },
soft_numbers = { type = 'boolean', default = true }, -- TODO: Add support for maximally precise numbers.
max_depth = { type = 'number', default = math.huge },
embed_loaded_funcs = { type = 'boolean', default = false },
more_function_info = { type = 'boolean', default = false },
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} },
short_builtins = { type = 'boolean', default = false },

View File

@ -135,18 +135,21 @@ end
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () end')(),
expect = 'function () end',
}
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')(),
expect = 'function () return function () end end',
}
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end\nend')()(),
expect = 'function () end',
}
@ -154,6 +157,7 @@ format_test {
format_test {
-- NOTE: This is HARD to fix. It's thus longerterm
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')()(),
expect = 'function () end',
}
@ -161,14 +165,14 @@ format_test {
format_test {
-- 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')(),
options = { more_function_info = true },
options = { embed_loaded_funcs = true },
expect = 'function (a, b) return a + b end',
}
format_test {
-- 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')(),
options = { more_function_info = true },
options = { embed_loaded_funcs = true },
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}',
}
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 {
-- No special indent if no special function modifier.
adv_getlocal = true,