diff --git a/common.lua b/common.lua index 15571de..2395513 100644 --- a/common.lua +++ b/common.lua @@ -30,6 +30,7 @@ local function iterate_utf8_chars (str) end local function utf8_string_length (str) + assert(type(str) == 'string') local len = 0 for char in iterate_utf8_chars(str) do if not UNICODE_ZERO_WIDTH_CHARACTERS[char] then diff --git a/pretty.lua b/pretty.lua index 7b45b3a..53de8ce 100644 --- a/pretty.lua +++ b/pretty.lua @@ -72,12 +72,17 @@ local ERROR_UNKNOWN_TYPE = [[ We are attempting to cover all Lua features, so please report this bug, so we can improve. ]] +local TERMINAL_WIDTH = 80 local MAX_WIDTH_FOR_SINGLE_LINE_TABLE = 38 + if io and io.popen then local f = io.popen "tput cols" local term_width = f:read '*n' f:close() --- if term_width then MAX_WIDTH_FOR_SINGLE_LINE_TABLE = term_width * 3 / 2 end + if term_width then + TERMINAL_WIDTH = term_width + --MAX_WIDTH_FOR_SINGLE_LINE_TABLE = term_width * (2 / 3) + end end local KEY_TYPE_SORT_ORDER = { @@ -313,7 +318,7 @@ local function align_into_columns (l, start_i, stop_i) for nr_items_pr_row = 10, 1, -1 do -- TODO: Do this more intelligently. local column_width column_width, columns = attempt_to_align_into_columns(l, start_i, stop_i, nr_items_pr_row) - if column_width <= MAX_WIDTH_FOR_SINGLE_LINE_TABLE then break end + if column_width <= l.options.max_width_for_single_line_table then break end end -- Change alignment of columns @@ -501,12 +506,14 @@ local function format_table (t, display, l, format_value) -- 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 + + if table_width <= l.options.max_width_for_single_line_table then -- Is short table: Ignore "width of key". l[start_of_table_i][3] = 'inline' ignore_alignment_info(l, start_of_table_i) elseif table_info.is_leaf_node then -- Is leaf node: Can format into columns. + -- Only if long or sequence. -- NOTE: Currently we only allow leaf-nodes to format into columns, due -- to issues with table alignment. align_into_columns(l, start_of_table_i) @@ -562,7 +569,6 @@ local TYPE_TO_FORMAT_FUNC = { local function format_value (value, display, l) assert(type(display) == 'number' and type(l) == 'table') local formatting = TYPE_TO_FORMAT_FUNC[type(value)] - --print(value, formatting) if formatting then formatting(value, display, l, format_value) else @@ -588,6 +594,8 @@ local KNOWN_OPTIONS = { _table_addr_comment = { type = 'boolean', default = false, debug = 'debug' }, -- TODO: Maybe automatically display table address when display = 0? indent = { type = 'string', default = ' ' }, + + max_output_width = { type = 'number', default = TERMINAL_WIDTH } } local function ensure_that_all_options_are_known (input_options) @@ -622,10 +630,36 @@ local function ensure_that_all_options_are_known (input_options) output_options[option_name] = option_info.default end end + -- Calculate derived settings + output_options.max_width_for_single_line_table = MAX_WIDTH_FOR_SINGLE_LINE_TABLE -- TODO: Make dynamic + -- Returns input_options return output_options end +local function length_of_longest_line_in_text (text) + assert(type(text) == 'string') + local longest_line_len = text:match '([^\n]*)$' : len() + for line in text:gmatch '(.-)\n' do + longest_line_len = math.max(longest_line_len, line:len()) + end + return longest_line_len +end + +local function internal_warning (fmt, ...) + io.stderr:write('[pretty/internal]: '..string.format(fmt, ...)) +end + +local function assert_pretty_result (repr, options) + assert(type(repr) == 'string') + assert(type(options) == 'table') + -- Determine length of longest line in output + local max_width = length_of_longest_line_in_text(repr) + if max_width > options.max_output_width then + internal_warning('Internal assertion failed. Width of output was %i, but should be less than %i.', max_width, options.max_output_width) + end +end + local function pretty_format (value, options) -- Error checking local options = ensure_that_all_options_are_known(options or {}) @@ -642,7 +676,12 @@ local function pretty_format (value, options) fix_seperator_info(l, l.options.indent) ignore_alignment_info(l) - return table.concat(l, '') + -- Concat and perform last assertions. + local repr = table.concat(l, '') + assert_pretty_result(repr, options) + + -- Return + return repr end return pretty_format diff --git a/test/test_pretty.lua b/test/test_pretty.lua index be40791..e9f7438 100644 --- a/test/test_pretty.lua +++ b/test/test_pretty.lua @@ -34,6 +34,15 @@ local function format_test (t) end, { line = debug.getinfo(2).currentline }) end +local function format_parsable_test (t) + local stripped = t.text:match '^%s*(.-)%s*$' + return format_test { + name = t.name, + input = loadstring('return '..stripped)(), + expect = stripped + } +end + -------------------------------------------------------------------------------- -- Primitive types @@ -263,6 +272,16 @@ format_test { expect = '{\n \'hello\', \'world\', \'how\',\n \'is\', \'it\', \'going?\',\n \'Im\', \'doing great\', \'thanks\',\n \'that\', \'was\', \'what\',\n \'I\'\n}', } +format_parsable_test { + name = 'Column style with aligned numbers', + text = [[ +{ + 200, -522, 423, 516, 523, 126, 2912, + 523, -620, 0, 0, 0, -5, 2, + 72, 6 +} +]] } + format_test { name = 'Tabular style with strings left aligned', input = {