1
0

Moved strings and their tests to new file.

This commit is contained in:
Jon Michael Aanes 2017-07-20 19:20:29 +02:00
parent 33fb88fdd7
commit 1f20c29f68
4 changed files with 228 additions and 174 deletions

View File

@ -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.
format_number = import('number', true) or function (value, _, l) l[#l+1] = tostring(value) end -- Will use a very simple string formatting, if string.lua is not available.
format_function = import('function', true) or function (value, _, l) l[#l+1] = 'function (...) --[['..tostring(value):sub(11)..']] end' 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_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
View 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

View File

@ -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
View 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