diff --git a/analyze_structure.lua b/analyze_structure.lua index b29b840..67fc22d 100644 --- a/analyze_structure.lua +++ b/analyze_structure.lua @@ -1,4 +1,6 @@ +local TABLE_TYPE = require "table_type" + local RESERVED_LUA_WORDS = { ['and'] = true, ['break'] = true, @@ -180,12 +182,12 @@ local function get_table_info (t) info.is_short = is_short_table(t) -- Determine type of table - if not info.has_seq and not info.has_map then info.type = 'empty' - elseif info.has_seq and not info.has_map then info.type = 'sequence' - elseif info.is_set then info.type = 'set' - elseif info.has_seq then info.type = 'mixed' - elseif contains_only_nice_string_keys(t) then info.type = 'string_map' - else info.type = 'pure_map' + if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY + elseif info.has_seq and not info.has_map then info.type = TABLE_TYPE.SEQUENCE + elseif info.is_set then info.type = TABLE_TYPE.SET + elseif info.has_seq then info.type = TABLE_TYPE.MIXED + elseif contains_only_nice_string_keys(t) then info.type = TABLE_TYPE.STRING_MAP + else info.type = TABLE_TYPE.PURE_MAP end return info diff --git a/pretty.lua b/pretty.lua index f2f5fbf..40e0a8b 100644 --- a/pretty.lua +++ b/pretty.lua @@ -2,7 +2,7 @@ -- Ensure loading library, if it exists, no matter where pretty.lua was loaded from. -- Load the library component -local LIBRARY, format_number +local LIBRARY, format_number, analyze_structure, TABLE_TYPE do local thispath = ... and select('1', ...):match('.+%.') or '' @@ -12,25 +12,22 @@ do -- Load number formatting was_loaded, format_number = pcall(require, thispath..'number') + was_loaded, analyze_structure = pcall(require, thispath..'analyze_structure') + was_loaded, TABLE_TYPE = pcall(require, thispath..'table_type') end -- local ERROR_UNKNOWN_TYPE = [[ [pretty]: Attempting to format unsupported value of type "%s". A native formatting of the value is: %s - This is a bug, and should be reported. + +We are attempting to cover all Lua features, so please report this bug, so we can improve. ]] -local TABLE_TYPE_EMPTY = 'EMPTY TABLE' -local TABLE_TYPE_SEQUENCE = 'SEQUENCE' -local TABLE_TYPE_STRING_MAP = 'STRING KEY MAP' -local TABLE_TYPE_PURE_MAP = 'PURE MAP' -local TABLE_TYPE_MIXED = 'MIXED TABLE' - local SINGLE_LINE_TABLE_TYPES = { - [TABLE_TYPE_SEQUENCE] = true, - [TABLE_TYPE_PURE_MAP] = true, - [TABLE_TYPE_STRING_MAP] = true, + [TABLE_TYPE.SEQUENCE] = true, + [TABLE_TYPE.PURE_MAP] = true, + [TABLE_TYPE.STRING_MAP] = true, } local SINGLE_LINE_SEQ_MAX_ELEMENTS = 10 @@ -335,17 +332,17 @@ local function get_table_type (value) -- * Pure Map: #value == 0 -- * Mixed: Any other - if is_empty_table(value) then return TABLE_TYPE_EMPTY end + if is_empty_table(value) then return TABLE_TYPE.EMPTY end local is_sequence = contains_only_nice_number_indexes(value) local only_string_keys = contains_only_nice_string_keys(value) local is_pure_map = (#value == 0) -- Return type - if is_sequence then return TABLE_TYPE_SEQUENCE - elseif only_string_keys then return TABLE_TYPE_STRING_MAP - elseif is_pure_map then return TABLE_TYPE_PURE_MAP - else return TABLE_TYPE_MIXED + if is_sequence then return TABLE_TYPE.SEQUENCE + elseif only_string_keys then return TABLE_TYPE.STRING_MAP + elseif is_pure_map then return TABLE_TYPE.PURE_MAP + else return TABLE_TYPE.MIXED end end @@ -393,10 +390,10 @@ local function format_key_and_value_sequence (l, key, value, options, depth) end local TABLE_TYPE_TO_PAIR_FORMAT = { - [TABLE_TYPE_SEQUENCE] = format_key_and_value_sequence, - [TABLE_TYPE_STRING_MAP] = format_key_and_value_string_map, - [TABLE_TYPE_PURE_MAP] = format_key_and_value_arbitr_map, - [TABLE_TYPE_MIXED] = format_key_and_value_arbitr_map, + [TABLE_TYPE.SEQUENCE] = format_key_and_value_sequence, + [TABLE_TYPE.STRING_MAP] = format_key_and_value_string_map, + [TABLE_TYPE.PURE_MAP] = format_key_and_value_arbitr_map, + [TABLE_TYPE.MIXED] = format_key_and_value_arbitr_map, } -- Formatting tables @@ -450,10 +447,15 @@ local function format_map (t, options, depth, l) end function format_table (t, options, depth, l) - local table_type = get_table_type(t) + local info = l.info[t] or {} + --local table_type = get_table_type(t) + + if options.recursion == 'marked' and info.marker then + l[#l+1], l[#l+2], l[#l+3] = '<', info.marker, '>' + end -- Empty or exteeding max-depth? - if table_type == TABLE_TYPE_EMPTY then l[#l+1] = '{}'; return + if info.type == TABLE_TYPE.EMPTY then l[#l+1] = '{}'; return elseif depth ~= 'max' and depth >= options.max_depth or l.visited[t] then l[#l+1] = '{...}'; return end @@ -641,10 +643,10 @@ local TYPE_TO_FORMAT_FUNC = { ['cdata'] = format_primitive, -- Luajit exclusive ? } -function format_value (value, options, depth, l) +function format_value (value, _, depth, l) local format_func = TYPE_TO_FORMAT_FUNC[type(value)] if format_func then - format_func(value, options, depth, l) + format_func(value, l.options, depth, l) else error(ERROR_UNKNOWN_TYPE:format(type(value), tostring(value)), 2) end @@ -654,10 +656,11 @@ end local function pretty_format (value, options) local l = { visited = { next_mark = 1 } } - local options = options or {} - options.max_depth = options.max_depth or math.huge - options.indent = options.indent or '\t' - format_value(value, options, 0, l) + l.options = options or {} + l.options.max_depth = l.options.max_depth or math.huge + l.options.indent = l.options.indent or '\t' + l.info = (type(value) == 'table') and analyze_structure(value) or {} + format_value(value, nil, 0, l) -- If any alignment info still exists, ignore it ignore_alignment_info(l) diff --git a/table_type.lua b/table_type.lua new file mode 100644 index 0000000..6a84761 --- /dev/null +++ b/table_type.lua @@ -0,0 +1,22 @@ + +-------------------------------------------------------------------------------- +-- Enum + +local enum_metatable = { + __tostring = function (e) return 'Enum:' .. e.name or 'Enum: no name' end, + __concat = function (a, b) return tostring(a) .. tostring(b) end, +} + +local function enum (t) + local e = {} + for _, v in ipairs(t) do + e[v] = setmetatable({ name = v }, enum_metatable) + end + return e +end + +-------------------------------------------------------------------------------- + +local TABLE_TYPE = enum { 'EMPTY', 'SEQUENCE', 'STRING_MAP', 'PURE_MAP', 'MIXED', 'SET' } + +return TABLE_TYPE diff --git a/test/test_analyze_structure.lua b/test/test_analyze_structure.lua index e636c8c..7b35dd3 100644 --- a/test/test_analyze_structure.lua +++ b/test/test_analyze_structure.lua @@ -1,6 +1,9 @@ local SUITE = require('TestSuite').new('analyze_structure') -SUITE:setEnviroment { analyze_structure = require('analyze_structure') } +SUITE:setEnviroment { + analyze_structure = require('analyze_structure'), + TABLE_TYPE = require('table_type') +} -------------------------------------------------------------------------------- -- analyze_structure @@ -9,7 +12,7 @@ SUITE:addTest('Empty Table', function () local input = {} -- Empty! local table_info = analyze_structure(input)[input] - assert(table_info.type == 'empty', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.EMPTY, 'Returned bad type: '..table_info.type) assert(table_info.has_seq == false) assert(table_info.has_map == false) end) @@ -18,7 +21,7 @@ SUITE:addTest('Sequence', function () local input = { 1, 2, 3 } local table_info = analyze_structure(input)[input] - assert(table_info.type == 'sequence', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert(table_info.has_seq == true) assert(table_info.has_map == false) end) @@ -27,7 +30,7 @@ SUITE:addTest('Pure Map', function () local input = { a = 1, [true] = 2, c = 3 } local table_info = analyze_structure(input)[input] - assert(table_info.type == 'pure_map', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) assert(table_info.has_seq == false) assert(table_info.has_map == true) end) @@ -36,7 +39,7 @@ SUITE:addTest('String Map', function () local input = { a = 1, b = 2, c = 3 } local table_info = analyze_structure(input)[input] - assert(table_info.type == 'string_map', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.STRING_MAP, 'Returned bad type: '..table_info.type) assert(table_info.has_seq == false) assert(table_info.has_map == true) end) @@ -45,7 +48,7 @@ SUITE:addTest('Set', function () local input = { a = true, b = true, c = true } local table_info = analyze_structure(input)[input] - assert(table_info.type == 'set', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.SET, 'Returned bad type: '..table_info.type) assert(table_info.is_set == true) end) @@ -133,7 +136,7 @@ SUITE:addTest('goto is special', function () local input = { ['goto'] = 'hi' } local table_info = analyze_structure(input)[input] - assert(table_info.type == 'pure_map', 'Returned bad type: '..table_info.type) + assert(table_info.type == TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) end) --------------------------------------------------------------------------------