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.
|
||||
|
||||
-- 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
|
||||
local thispath = ... and select('1', ...):match('.+%.') or ''
|
||||
local function import (name, ignore_failure)
|
||||
|
@ -56,14 +56,18 @@ do
|
|||
-- Load number and function formatting
|
||||
-- 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 string formatting, if string.lua is not available.
|
||||
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_string = import('pstring', true) or function (value, _, l) l[#l+1] = '[['..value..']]' end
|
||||
|
||||
-- Load other stuff
|
||||
analyze_structure = import 'analyze_structure'
|
||||
TABLE_TYPE = import 'table_type'
|
||||
end
|
||||
--
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Constants
|
||||
|
||||
local ERROR_UNKNOWN_TYPE = [[
|
||||
[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.
|
||||
]]
|
||||
|
||||
local NR_CHARS_IN_LONG_STRING = 40
|
||||
local MAX_WIDTH_FOR_SINGLE_LINE_TABLE = 38
|
||||
|
||||
local KEY_TYPE_SORT_ORDER = {
|
||||
|
@ -96,24 +99,8 @@ local VALUE_TYPE_SORT_ORDER = {
|
|||
['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 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)
|
||||
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
|
||||
|
||||
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
|
||||
--------------------------------------------------------------------------------
|
||||
-- Formatting Util
|
||||
|
||||
local function width_of_strings_in_l (l, start_i, stop_i)
|
||||
|
||||
|
@ -495,53 +459,6 @@ end
|
|||
|
||||
-- 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)
|
||||
-- Formats a coroutine. Unfortunantly we cannot gather a lot of information
|
||||
|
@ -572,7 +489,7 @@ local TYPE_TO_FORMAT_FUNC = {
|
|||
['thread'] = format_coroutine,
|
||||
['table'] = format_table,
|
||||
|
||||
['function'] = format_function, -- TODO: Improve a little
|
||||
['function'] = format_function,
|
||||
['userdata'] = format_primitive, -- TODO
|
||||
['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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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
|
||||
|
||||
|
|
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