115 lines
3.7 KiB
Lua
115 lines
3.7 KiB
Lua
|
|
-- 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
|