From 9a6a5b4b51ec133d50cf2598cd93d3c14e1d29ef Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Sat, 24 Jun 2017 20:06:36 +0200 Subject: [PATCH] Function formatting will now automatically pull documentation from a lua file, if it knows where to look. --- function.lua | 57 ++++++++++++++++++-------- pretty.lua | 2 +- test/test_analyze_structure.lua | 22 +++++----- test/test_function.lua | 71 ++++++++++++++++++++++++++++++--- 4 files changed, 119 insertions(+), 33 deletions(-) diff --git a/function.lua b/function.lua index 060f606..e991ba6 100644 --- a/function.lua +++ b/function.lua @@ -123,12 +123,23 @@ local function get_function_paramlist_and_body (str, start_line, end_line) local start_line_index = get_line_index(str, start_line) local end_line_index = get_line_index(str, end_line + 1) - 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') + local function_params, function_body = str:sub(start_line_index, end_line_index):match('.*%f[%a_]function%f[^%a_]%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.+)[ \t]*end') function_body = function_body:match('^%s*(.-)%s*$') assert(type(function_params) == 'string' and type(function_body) == 'string') return function_params, function_body end +local function get_function_body_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 select(2, get_function_paramlist_and_body(str, start_line, end_line)) +end + local function get_full_function_str (...) local function_params, function_body = get_function_paramlist_and_body(...) return 'function '..function_params..' '..function_body..' end' @@ -159,14 +170,20 @@ local function add_indent_to_string (str, indent) assert(type(str) == 'string') assert(type(indent) == 'string') - local l = {} - for line in str:gmatch('[^\n]+', true) do - l[#l+1] = indent - l[#l+1] = line - l[#l+1] = '\n' + return indent .. str:gsub('\n', '\n'..indent) +end + +local function get_docs_from_function_body (func_body) + assert(type(func_body) == 'string') + local doc_lines = {} + for line in func_body:gmatch('[^\n]+', true) do + if not line:match('^%s*$') then + local line_text = line:match('^%s*%-%-%s*(.*)%s*$') + if not line_text then break end + doc_lines[#doc_lines+1] = line_text + end end - if l[#l] == '\n' then l[#l] = nil end - return table.concat(l, '') + return table.concat(doc_lines, '\n') end -------------------------------------------------------------------------------- @@ -215,6 +232,10 @@ return function (value, depth, l, format_value) if info.defined_how == 'string' and l.options.embed_loaded_funcs then -- Function was defined as a string. function_params, function_body = get_function_paramlist_and_body(info.source, info.linedefined, info.lastlinedefined) + --elseif info.defined_how == 'file' then + --function_body = get_function_body_from_file(info.short_src, info.linedefined, info.lastlinedefined) + ----print(function_body) + --function_body = function_body or '...' end if info.builtin and l.options.short_builtins then @@ -259,17 +280,21 @@ return function (value, depth, l, format_value) end -- Doc - if info.doc then - for doc_line in info.doc:gmatch('[^\n]+') do - l[#l+1] = indent - l[#l+1] = '-- ' + if not info.doc then + local function_body = get_function_body_from_file(info.short_src, info.linedefined, info.lastlinedefined) + if function_body then + local documentation = get_docs_from_function_body(function_body) + info.doc = documentation ~= '' and documentation + end + end - l[#l+1] = doc_line - end + if info.doc then + l[#l+1] = '\n' + l[#l+1] = add_indent_to_string(info.doc, l.options.indent .. '-- ') end -- source - if not info.builtin then + if not info.builtin and not info.doc then l[#l+1] = indent l[#l+1] = ('-- source_file: \'%s\' '):format(info.short_src) if info.linedefined == info.lastlinedefined then @@ -280,7 +305,7 @@ return function (value, depth, l, format_value) end -- upvalues - if info.nups > 0 and not info.builtin then + if info.nups > 0 and (not info.builtin and not info.doc) then l[#l+1] = indent l[#l+1] = '-- up_values: ' format_value(info.ups, depth + 1, l) diff --git a/pretty.lua b/pretty.lua index 4498a53..d4a7a61 100644 --- a/pretty.lua +++ b/pretty.lua @@ -474,8 +474,8 @@ local KNOWN_OPTIONS = { indent = { type = 'string', default = ' ' }, max_depth = { type = 'number', default = math.huge }, embed_loaded_funcs = { type = 'boolean', default = false }, -- TODO: Outphase this, in favor of automatically embedding "small enough" functions. - recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} }, short_builtins = { type = 'boolean', default = false }, -- TODO: Outphase this. Rather automatically use the short versions in places where it would be strange to find the function, like keys, etc. + recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} }, } local function ensure_that_all_options_are_known (options) diff --git a/test/test_analyze_structure.lua b/test/test_analyze_structure.lua index 73aff59..597858d 100644 --- a/test/test_analyze_structure.lua +++ b/test/test_analyze_structure.lua @@ -166,7 +166,7 @@ SUITE:addTest('Recursive Numeration, Simple', function () input[1] = input local info = analyze_structure(input) - assert(info[input].marker == 1) + assert_equal(1, info[input].marker) end) SUITE:addTest('Recursive Numeration, Multiple-appear', function () @@ -174,7 +174,7 @@ SUITE:addTest('Recursive Numeration, Multiple-appear', function () input[1][1] = input[2][1] local info = analyze_structure(input) - assert(info[input[1][1]].marker == 1) + assert_equal(1, info[input[1][1]].marker) end) SUITE:addTest('Recursive Numeration, Multiple at once', function () @@ -183,8 +183,8 @@ SUITE:addTest('Recursive Numeration, Multiple at once', function () input[1][2] = input local info = analyze_structure(input) - assert(type(info[input[1][1]].marker) == 'number') - assert(type(info[input].marker) == 'number') + assert_equal('number', type(info[input[1][1]].marker)) + assert_equal('number', type(info[input].marker)) end) SUITE:addTest('Recursive Numeration, Through Keys', function () @@ -192,7 +192,7 @@ SUITE:addTest('Recursive Numeration, Through Keys', function () input[input] = input local info = analyze_structure(input) - assert(info[input].marker == 1) + assert_equal(1, info[input].marker) end) -------------------------------------------------------------------------------- @@ -209,7 +209,7 @@ SUITE:addTest('Can count elements, even though metatable.__index throws errors', local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end}) local info = analyze_structure(input) - assert_equal(info[input].seq_elems, 2) + assert_equal(2, info[input].seq_elems) end) -------------------------------------------------------------------------------- @@ -219,21 +219,21 @@ SUITE:addTest('Can detect uniform structure', function () local input = { a = 'b', b = 'a' } local info = analyze_structure(input) - assert_equal(info[input].is_uniform, true) + assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect uniform structure with different key value types', function () local input = { a = 1, b = 4 } local info = analyze_structure(input) - assert_equal(info[input].is_uniform, true) + assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect basic non-uniform structure', function () local input = { a = 'b', b = 5 } local info = analyze_structure(input) - assert_equal(info[input].is_uniform, false) + assert_equal(false, info[input].is_uniform) end) SUITE:addTest('Can detect pseudo-uniform structure with nested tables', function () @@ -242,14 +242,14 @@ SUITE:addTest('Can detect pseudo-uniform structure with nested tables', function local input = { a = { d = 7 }, b = { p = 3 } } local info = analyze_structure(input) - assert_equal(info[input].is_uniform, true) + assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect non-uniform structure with nested tables', function () local input = { a = { 'a', 'b', 'c' }, b = { p = 3, 'hi' } } local info = analyze_structure(input) - assert_equal(info[input].is_uniform, false) + assert_equal(false, info[input].is_uniform) end) -- TODO: Add predicate to check for pseudo-uniformness. diff --git a/test/test_function.lua b/test/test_function.lua index eab743f..41a85ff 100644 --- a/test/test_function.lua +++ b/test/test_function.lua @@ -119,15 +119,67 @@ format_test { expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { TEST_UPVALUE = 42 }\n\n ...\nend' } -local func_line = curline(3) +-------------------------------------------------------------------------------- +-- Elaborate functions with documentation + format_test { - name = 'Elaborate function with documentation', + name = 'Basic Func with docs', input = function () - -- Hello World - if true then return false end + -- This is docs return true end, - expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+1)..']\n -- Hello World\n\n ...\nend', + expect = 'function ()\n -- This is docs\n\n ...\nend', +} + +format_test { + name = 'Comments after other code won\'t be included as docs', + input = function () + -- This is also docs + if true then return false end + -- Some other comment, that won't appear as docs. + return true + end, + expect = 'function ()\n -- This is also docs\n\n ...\nend', +} + +format_test { + name = 'We can leave a space between doc lines', + input = function () + -- This is docs + + -- This is also docs + return true + end, + expect = 'function ()\n -- This is docs\n -- This is also docs\n\n ...\nend', +} + +format_test { + name = 'We can also leave a line with an empty comment', + input = function () + -- This is docs + -- + -- This is also docs + return true + end, + expect = 'function ()\n -- This is docs\n -- \n -- This is also docs\n\n ...\nend', +} + +format_test { + name = 'No opvalues when docs are there', + input = function () + -- This is docs + return TEST_UPVALUE + end, + expect = 'function ()\n -- This is docs\n\n ...\nend', +} + +format_test { + name = 'Can find docs when body contains the word "function"', + input = function () + -- Hi + _function() + end, + expect = 'function ()\n -- Hi\n\n ...\nend', } -------------------------------------------------------------------------------- @@ -229,6 +281,15 @@ format_test { expect = 'function () ... end', } +format_test { + name = 'Can still find body when body contains the word "function"', + single = true, + adv_getlocal = true, + options = { embed_loaded_funcs = true }, + input = loadstring('return function () function_body() end')(), + expect = 'function () function_body() end', +} + -------------------------------------------------------------------------------- -- Indent functions nicely