Moved strings and their tests to new file.
This commit is contained in:
parent
33fb88fdd7
commit
1f20c29f68
103
pretty.lua
103
pretty.lua
|
@ -41,7 +41,7 @@ a table, we have a better idea, but then the output would be cluttered.
|
||||||
-- Ensure loading library, if it exists, no matter where pretty.lua was loaded from.
|
-- Ensure loading library, if it exists, no matter where pretty.lua was loaded from.
|
||||||
|
|
||||||
-- Load the library component
|
-- Load the library component
|
||||||
local format_number, format_function, analyze_structure, TABLE_TYPE
|
local format_number, format_function, format_string, analyze_structure, TABLE_TYPE
|
||||||
do
|
do
|
||||||
local thispath = ... and select('1', ...):match('.+%.') or ''
|
local thispath = ... and select('1', ...):match('.+%.') or ''
|
||||||
local function import (name, ignore_failure)
|
local function import (name, ignore_failure)
|
||||||
|
@ -56,14 +56,18 @@ do
|
||||||
-- Load number and function formatting
|
-- Load number and function formatting
|
||||||
-- Will use a very simple number formatting, if number.lua is not available.
|
-- Will use a very simple number formatting, if number.lua is not available.
|
||||||
-- Will use a very simple function formatting, if function.lua is not available.
|
-- Will use a very simple function formatting, if function.lua is not available.
|
||||||
|
-- Will use a very simple string formatting, if string.lua is not available.
|
||||||
format_number = import('number', true) or function (value, _, l) l[#l+1] = tostring(value) end
|
format_number = import('number', true) or function (value, _, l) l[#l+1] = tostring(value) end
|
||||||
format_function = import('function', true) or function (value, _, l) l[#l+1] = 'function (...) --[['..tostring(value):sub(11)..']] end' end
|
format_function = import('function', true) or function (value, _, l) l[#l+1] = 'function (...) --[['..tostring(value):sub(11)..']] end' end
|
||||||
|
format_string = import('pstring', true) or function (value, _, l) l[#l+1] = '[['..value..']]' end
|
||||||
|
|
||||||
-- Load other stuff
|
-- Load other stuff
|
||||||
analyze_structure = import 'analyze_structure'
|
analyze_structure = import 'analyze_structure'
|
||||||
TABLE_TYPE = import 'table_type'
|
TABLE_TYPE = import 'table_type'
|
||||||
end
|
end
|
||||||
--
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Constants
|
||||||
|
|
||||||
local ERROR_UNKNOWN_TYPE = [[
|
local ERROR_UNKNOWN_TYPE = [[
|
||||||
[pretty]: Attempting to format unsupported value of type "%s".
|
[pretty]: Attempting to format unsupported value of type "%s".
|
||||||
|
@ -72,7 +76,6 @@ local ERROR_UNKNOWN_TYPE = [[
|
||||||
We are attempting to cover all Lua features, so please report this bug, so we can improve.
|
We are attempting to cover all Lua features, so please report this bug, so we can improve.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local NR_CHARS_IN_LONG_STRING = 40
|
|
||||||
local MAX_WIDTH_FOR_SINGLE_LINE_TABLE = 38
|
local MAX_WIDTH_FOR_SINGLE_LINE_TABLE = 38
|
||||||
|
|
||||||
local KEY_TYPE_SORT_ORDER = {
|
local KEY_TYPE_SORT_ORDER = {
|
||||||
|
@ -96,24 +99,8 @@ local VALUE_TYPE_SORT_ORDER = {
|
||||||
['function'] = 7,
|
['function'] = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
local CHAR_TO_STR_REPR = {}
|
|
||||||
|
|
||||||
do
|
|
||||||
for i = 00, 031 do CHAR_TO_STR_REPR[i] = '\\0'..(i < 10 and '0' or '')..i end
|
|
||||||
for i = 32, 255 do CHAR_TO_STR_REPR[i] = string.char(i) end
|
|
||||||
CHAR_TO_STR_REPR[7] = '\\a'
|
|
||||||
CHAR_TO_STR_REPR[8] = '\\b'
|
|
||||||
CHAR_TO_STR_REPR[9] = '\t'
|
|
||||||
CHAR_TO_STR_REPR[10] = '\n'
|
|
||||||
CHAR_TO_STR_REPR[11] = '\\v'
|
|
||||||
CHAR_TO_STR_REPR[12] = '\\f'
|
|
||||||
CHAR_TO_STR_REPR[13] = '\\r'
|
|
||||||
CHAR_TO_STR_REPR[92] = '\\\\'
|
|
||||||
CHAR_TO_STR_REPR[127] = '\\127'
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Util
|
-- Key-value-pair Util
|
||||||
|
|
||||||
local function padnum(d)
|
local function padnum(d)
|
||||||
local dec, n = string.match(d, "(%.?)0*(.+)")
|
local dec, n = string.match(d, "(%.?)0*(.+)")
|
||||||
|
@ -197,31 +184,8 @@ local function fill_holes_in_key_value_pairs (key_value_pairs)
|
||||||
table.sort(key_value_pairs, compare_key_value_pair)
|
table.sort(key_value_pairs, compare_key_value_pair)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function smallest_secure_longform_string_level (str)
|
--------------------------------------------------------------------------------
|
||||||
-- Determines the level a longform string needs to use, to avoid code
|
-- Formatting Util
|
||||||
-- injection. For example, if we want to use longform on the string
|
|
||||||
-- 'Hello ]] World', we cannot use level-0 as this would result in
|
|
||||||
-- '[[Hello ]] World]]', which could be an issue in certain applications.
|
|
||||||
|
|
||||||
-- Error checking
|
|
||||||
assert(type(str) == 'string')
|
|
||||||
|
|
||||||
-- Do stuff
|
|
||||||
local levels = { [1] = 1 }
|
|
||||||
str:gsub('%]=*%]', function (m) levels[m:len()] = true end)
|
|
||||||
return #levels - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function escape_string (str)
|
|
||||||
|
|
||||||
-- Error checking
|
|
||||||
assert(type(str) == 'string')
|
|
||||||
|
|
||||||
-- Do stuff
|
|
||||||
local l = {}
|
|
||||||
for i = 1, #str do l[#l+1] = CHAR_TO_STR_REPR[str:byte(i)] end
|
|
||||||
return table.concat(l, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
local function width_of_strings_in_l (l, start_i, stop_i)
|
local function width_of_strings_in_l (l, start_i, stop_i)
|
||||||
|
|
||||||
|
@ -495,53 +459,6 @@ end
|
||||||
|
|
||||||
-- Formatting Strings
|
-- Formatting Strings
|
||||||
|
|
||||||
local function format_string (str, depth, l)
|
|
||||||
-- TODO: Add option for escaping unicode characters.
|
|
||||||
-- TODO: Improve cutstring argument.
|
|
||||||
-- TODO: Move this to it's own file.
|
|
||||||
|
|
||||||
-- Error checking
|
|
||||||
assert( type(str) == 'string' )
|
|
||||||
assert(type(depth) == 'number' and type(l) == 'table')
|
|
||||||
|
|
||||||
-- Do work
|
|
||||||
|
|
||||||
local is_long_string = (str:len() >= NR_CHARS_IN_LONG_STRING)
|
|
||||||
local newline_or_tab_index = str:find('[\n\t]')
|
|
||||||
local single_quote_index = str:find('\'')
|
|
||||||
local double_quote_index = str:find('\"')
|
|
||||||
|
|
||||||
-- ...
|
|
||||||
local chance_of_longform = is_long_string and ((newline_or_tab_index or math.huge) <= NR_CHARS_IN_LONG_STRING) or double_quote_index and single_quote_index
|
|
||||||
local cut_string_index = l.options.cut_strings and (is_long_string or chance_of_longform)
|
|
||||||
and math.min(NR_CHARS_IN_LONG_STRING - 3, newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0)
|
|
||||||
|
|
||||||
local longform = chance_of_longform and ((not cut_string_index) or cut_string_index < math.min(newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0))
|
|
||||||
|
|
||||||
local escape_newline_and_tab = not longform and newline_or_tab_index
|
|
||||||
|
|
||||||
-- Determine string delimiters
|
|
||||||
local left, right
|
|
||||||
if longform then
|
|
||||||
local level = smallest_secure_longform_string_level(str)
|
|
||||||
left, right = '['..string.rep('=', level)..'[', ']'..string.rep('=', level)..']'
|
|
||||||
if newline_or_tab_index then str = '\n' .. str end
|
|
||||||
elseif not single_quote_index then
|
|
||||||
left, right = '\'', '\''
|
|
||||||
else
|
|
||||||
left, right = '\"', '\"'
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Cut string
|
|
||||||
if cut_string_index then str = str:sub(1, cut_string_index) end
|
|
||||||
str = escape_string(str)
|
|
||||||
-- Escape newline and tab
|
|
||||||
if escape_newline_and_tab then str = str:gsub('\n', '\\n'):gsub('\t', '\\t') end
|
|
||||||
|
|
||||||
l[#l+1] = left
|
|
||||||
l[#l+1] = str
|
|
||||||
l[#l+1] = right
|
|
||||||
end
|
|
||||||
|
|
||||||
local function format_coroutine (value, depth, l)
|
local function format_coroutine (value, depth, l)
|
||||||
-- Formats a coroutine. Unfortunantly we cannot gather a lot of information
|
-- Formats a coroutine. Unfortunantly we cannot gather a lot of information
|
||||||
|
@ -572,7 +489,7 @@ local TYPE_TO_FORMAT_FUNC = {
|
||||||
['thread'] = format_coroutine,
|
['thread'] = format_coroutine,
|
||||||
['table'] = format_table,
|
['table'] = format_table,
|
||||||
|
|
||||||
['function'] = format_function, -- TODO: Improve a little
|
['function'] = format_function,
|
||||||
['userdata'] = format_primitive, -- TODO
|
['userdata'] = format_primitive, -- TODO
|
||||||
['cdata'] = format_primitive, -- TODO & Luajit only
|
['cdata'] = format_primitive, -- TODO & Luajit only
|
||||||
}
|
}
|
||||||
|
|
114
pstring.lua
Normal file
114
pstring.lua
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
|
||||||
|
-- pretty.string
|
||||||
|
-- The string formatting module for pretty.
|
||||||
|
|
||||||
|
--[=[ Thoughts on displaying strings in the useful ways.
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
--]=]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Constants
|
||||||
|
|
||||||
|
local NR_CHARS_IN_LONG_STRING = 40
|
||||||
|
|
||||||
|
local CHAR_TO_STR_REPR = {}
|
||||||
|
|
||||||
|
do
|
||||||
|
for i = 00, 031 do CHAR_TO_STR_REPR[i] = ('\\%03i'):format(i) end
|
||||||
|
for i = 32, 255 do CHAR_TO_STR_REPR[i] = string.char(i) end
|
||||||
|
CHAR_TO_STR_REPR[7] = '\\a'
|
||||||
|
CHAR_TO_STR_REPR[8] = '\\b'
|
||||||
|
CHAR_TO_STR_REPR[9] = '\t'
|
||||||
|
CHAR_TO_STR_REPR[10] = '\n'
|
||||||
|
CHAR_TO_STR_REPR[11] = '\\v'
|
||||||
|
CHAR_TO_STR_REPR[12] = '\\f'
|
||||||
|
CHAR_TO_STR_REPR[13] = '\\r'
|
||||||
|
CHAR_TO_STR_REPR[92] = '\\\\'
|
||||||
|
CHAR_TO_STR_REPR[127] = '\\127'
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Util
|
||||||
|
|
||||||
|
local function escape_string (str)
|
||||||
|
-- Attempts to escape the string, to a format that is both a valid Lua
|
||||||
|
-- constant, and ledible unicode.
|
||||||
|
|
||||||
|
-- TODO: Escape invalid unicode sequences.
|
||||||
|
|
||||||
|
-- Error checking
|
||||||
|
assert(type(str) == 'string')
|
||||||
|
|
||||||
|
-- Do stuff
|
||||||
|
local l = {}
|
||||||
|
for i = 1, #str do l[#l+1] = CHAR_TO_STR_REPR[str:byte(i)] end
|
||||||
|
return table.concat(l, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function smallest_secure_longform_string_level (str)
|
||||||
|
-- Determines the level a longform string needs to use, to avoid code
|
||||||
|
-- injection. For example, if we want to use longform on the string
|
||||||
|
-- 'Hello ]] World', we cannot use level-0 as this would result in
|
||||||
|
-- '[[Hello ]] World]]', which could be an issue in certain applications.
|
||||||
|
|
||||||
|
-- Error checking
|
||||||
|
assert(type(str) == 'string')
|
||||||
|
|
||||||
|
-- Do stuff
|
||||||
|
local levels = { [1] = 1 }
|
||||||
|
str:gsub('%]=*%]', function (m) levels[m:len()] = true end)
|
||||||
|
return #levels - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return function (str, depth, l)
|
||||||
|
-- pretty.format_string
|
||||||
|
|
||||||
|
-- TODO: Add option for escaping unicode characters.
|
||||||
|
-- TODO: Improve cutstring argument.
|
||||||
|
|
||||||
|
-- Error checking
|
||||||
|
assert( type(str) == 'string' )
|
||||||
|
assert(type(depth) == 'number' and type(l) == 'table')
|
||||||
|
|
||||||
|
-- Do work
|
||||||
|
|
||||||
|
local is_long_string = (str:len() >= NR_CHARS_IN_LONG_STRING)
|
||||||
|
local newline_or_tab_index = str:find('[\n\t]')
|
||||||
|
local single_quote_index = str:find('\'')
|
||||||
|
local double_quote_index = str:find('\"')
|
||||||
|
|
||||||
|
-- ...
|
||||||
|
local chance_of_longform = is_long_string and ((newline_or_tab_index or math.huge) <= NR_CHARS_IN_LONG_STRING) or double_quote_index and single_quote_index
|
||||||
|
local cut_string_index = l.options.cut_strings and (is_long_string or chance_of_longform)
|
||||||
|
and math.min(NR_CHARS_IN_LONG_STRING - 3, newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0)
|
||||||
|
|
||||||
|
local longform = chance_of_longform and ((not cut_string_index) or cut_string_index < math.min(newline_or_tab_index or 1/0, double_quote_index or 1/0, single_quote_index or 1/0))
|
||||||
|
|
||||||
|
local escape_newline_and_tab = not longform and newline_or_tab_index
|
||||||
|
|
||||||
|
-- Determine string delimiters
|
||||||
|
local left, right
|
||||||
|
if longform then
|
||||||
|
local level = smallest_secure_longform_string_level(str)
|
||||||
|
left, right = '['..string.rep('=', level)..'[', ']'..string.rep('=', level)..']'
|
||||||
|
if newline_or_tab_index then str = '\n' .. str end
|
||||||
|
elseif not single_quote_index then
|
||||||
|
left, right = '\'', '\''
|
||||||
|
else
|
||||||
|
left, right = '\"', '\"'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cut string
|
||||||
|
if cut_string_index then str = str:sub(1, cut_string_index) end
|
||||||
|
str = escape_string(str)
|
||||||
|
-- Escape newline and tab
|
||||||
|
if escape_newline_and_tab then str = str:gsub('\n', '\\n'):gsub('\t', '\\t') end
|
||||||
|
|
||||||
|
l[#l+1] = left
|
||||||
|
l[#l+1] = str
|
||||||
|
l[#l+1] = right
|
||||||
|
end
|
|
@ -34,85 +34,6 @@ local function format_test (t)
|
||||||
end, { line = debug.getinfo(2).currentline })
|
end, { line = debug.getinfo(2).currentline })
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- Strings
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = 'Hello World',
|
|
||||||
expect = '\'Hello World\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = 'Hello \'World\'',
|
|
||||||
expect = '\"Hello \'World\'\"',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = 'Hello \"World\"',
|
|
||||||
expect = '\'Hello \"World\"\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = 'Hello [[World]]',
|
|
||||||
expect = '\'Hello [[World]]\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\'Hello\' [[World]]',
|
|
||||||
expect = '\"\'Hello\' [[World]]\"',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\'Hello\' \"there\" [[World]]',
|
|
||||||
expect = '[=[\'Hello\' \"there\" [[World]]]=]',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\'Hello\' \"there\" [=[World]=]',
|
|
||||||
expect = '[[\'Hello\' \"there\" [=[World]=]]]',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\nHello World',
|
|
||||||
expect = '\'\\nHello World\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\'\"\n',
|
|
||||||
expect = '[[\n\'\"\n]]',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\n',
|
|
||||||
expect = '\'\\n\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\\',
|
|
||||||
expect = '\'\\\\\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = '\000',
|
|
||||||
expect = '\'\\000\'',
|
|
||||||
}
|
|
||||||
format_test {
|
|
||||||
input = '\a\b\v\r\f',
|
|
||||||
expect = '\'\\a\\b\\v\\r\\f\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
input = 'ø',
|
|
||||||
expect = '\'ø\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
format_test {
|
|
||||||
name = 'Malformed Unicode is escaped',
|
|
||||||
input = '\000\001\003\012\169\003\000\030',
|
|
||||||
expect = '\'\\000\\000\\001\\003\\012\\169\\003\\000\\030\'',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Primitive types
|
-- Primitive types
|
||||||
|
|
||||||
|
|
102
test/test_pstring.lua
Normal file
102
test/test_pstring.lua
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
|
||||||
|
local SUITE = require 'TestSuite' 'string'
|
||||||
|
SUITE:setEnviroment{
|
||||||
|
format = require('pretty')
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- Compat
|
||||||
|
if not loadstring then loadstring = load end -- Lua 5.3 compat
|
||||||
|
--
|
||||||
|
|
||||||
|
local function format_test (t)
|
||||||
|
SUITE:addTest(t.name or t.expect, function ()
|
||||||
|
local actual_result = format(t.input, t.options)
|
||||||
|
assert_equal(t.expect, actual_result)
|
||||||
|
end, { line = debug.getinfo(2).currentline })
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 'Hello World',
|
||||||
|
expect = '\'Hello World\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 'Hello \'World\'',
|
||||||
|
expect = '\"Hello \'World\'\"',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 'Hello \"World\"',
|
||||||
|
expect = '\'Hello \"World\"\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 'Hello [[World]]',
|
||||||
|
expect = '\'Hello [[World]]\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\'Hello\' [[World]]',
|
||||||
|
expect = '\"\'Hello\' [[World]]\"',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\'Hello\' \"there\" [[World]]',
|
||||||
|
expect = '[=[\'Hello\' \"there\" [[World]]]=]',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\'Hello\' \"there\" [=[World]=]',
|
||||||
|
expect = '[[\'Hello\' \"there\" [=[World]=]]]',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\nHello World',
|
||||||
|
expect = '\'\\nHello World\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\'\"\n',
|
||||||
|
expect = '[[\n\'\"\n]]',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\n',
|
||||||
|
expect = '\'\\n\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\\',
|
||||||
|
expect = '\'\\\\\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = '\000',
|
||||||
|
expect = '\'\\000\'',
|
||||||
|
}
|
||||||
|
format_test {
|
||||||
|
input = '\a\b\v\r\f',
|
||||||
|
expect = '\'\\a\\b\\v\\r\\f\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Unicode
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
input = 'ø',
|
||||||
|
expect = '\'ø\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
format_test {
|
||||||
|
name = 'Malformed Unicode is escaped',
|
||||||
|
input = '\000\001\003\012\169\003\000\030',
|
||||||
|
expect = '\'\\000\\000\\001\\003\\012\\169\\003\\000\\030\'',
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
return SUITE
|
Loading…
Reference in New Issue
Block a user