Alternative process for determining short tables
This one is based on the representative width of the table. Not only does this produce better results, but it's also more futureproof.
This commit is contained in:
parent
5bee4a7378
commit
155c877987
|
@ -71,7 +71,6 @@ local function get_value_types (t)
|
||||||
return types
|
return types
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function largest_number_index (t)
|
local function largest_number_index (t)
|
||||||
-- Returns the largest number index in t.
|
-- Returns the largest number index in t.
|
||||||
|
|
||||||
|
@ -84,6 +83,14 @@ local function largest_number_index (t)
|
||||||
return max_index
|
return max_index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function nr_elements_in_map (t)
|
||||||
|
local k, count = nil, -1
|
||||||
|
repeat
|
||||||
|
k, count = next(t, k), count + 1
|
||||||
|
until not k
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function contains_only_nice_string_keys (t)
|
local function contains_only_nice_string_keys (t)
|
||||||
|
@ -200,6 +207,7 @@ local function get_table_info (t)
|
||||||
info.is_set = is_set(t)
|
info.is_set = is_set(t)
|
||||||
info.is_tabular = is_tabular(t)
|
info.is_tabular = is_tabular(t)
|
||||||
info.is_short = is_short_table(t)
|
info.is_short = is_short_table(t)
|
||||||
|
info.nr_elems = nr_elements_in_map(t) -- TODO: Use this for something.
|
||||||
|
|
||||||
-- Determine type of table
|
-- Determine type of table
|
||||||
if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY
|
if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY
|
||||||
|
|
145
pretty.lua
145
pretty.lua
|
@ -36,6 +36,7 @@ local SINGLE_LINE_TABLE_TYPES = {
|
||||||
local SINGLE_LINE_SEQ_MAX_ELEMENTS = 10
|
local SINGLE_LINE_SEQ_MAX_ELEMENTS = 10
|
||||||
local SINGLE_LINE_MAP_MAX_ELEMENTS = 5
|
local SINGLE_LINE_MAP_MAX_ELEMENTS = 5
|
||||||
local NR_CHARS_IN_LONG_STRING = 40
|
local NR_CHARS_IN_LONG_STRING = 40
|
||||||
|
local MAX_WIDTH_FOR_SINGLE_LINE_TABLE = 38
|
||||||
|
|
||||||
local KEY_TYPE_SORT_ORDER = {
|
local KEY_TYPE_SORT_ORDER = {
|
||||||
['number'] = 0,
|
['number'] = 0,
|
||||||
|
@ -171,14 +172,6 @@ local function fill_holes_in_key_value_pairs (key_value_pairs)
|
||||||
table.sort(key_value_pairs, compare_key_value_pairs)
|
table.sort(key_value_pairs, compare_key_value_pairs)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function nr_elements_in_map (t)
|
|
||||||
local k, count = nil, -1
|
|
||||||
repeat
|
|
||||||
k, count = next(t, k), count + 1
|
|
||||||
until not k
|
|
||||||
return count
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_identifier(str)
|
local function is_identifier(str)
|
||||||
-- An identier is defined in the lua reference guide
|
-- An identier is defined in the lua reference guide
|
||||||
|
|
||||||
|
@ -222,7 +215,7 @@ end
|
||||||
local function width_of_strings_in_l (l, start_i, end_i)
|
local function width_of_strings_in_l (l, start_i, end_i)
|
||||||
local width = 0
|
local width = 0
|
||||||
for i = start_i or 1, (end_i or #l) do
|
for i = start_i or 1, (end_i or #l) do
|
||||||
width = width + #l[i]
|
width = width + ((type(l[i]) ~= 'string') and 1 or #l[i])
|
||||||
end
|
end
|
||||||
return width
|
return width
|
||||||
end
|
end
|
||||||
|
@ -236,9 +229,11 @@ local function ignore_alignment_info (l, start_i, stop_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fix_alignment (l, start_i, stop_i)
|
local function fix_alignment (l, start_i, stop_i)
|
||||||
|
local start_i, stop_i = start_i or 1, stop_i or #l
|
||||||
|
|
||||||
-- Find maximums
|
-- Find maximums
|
||||||
local max = {}
|
local max = {}
|
||||||
for i = start_i or 1, stop_i or #l do
|
for i = start_i, stop_i do
|
||||||
if type(l[i]) == 'table' then
|
if type(l[i]) == 'table' then
|
||||||
max[ l[i][2] ] = math.max( l[i][1], max[ l[i][2] ] or 0 )
|
max[ l[i][2] ] = math.max( l[i][1], max[ l[i][2] ] or 0 )
|
||||||
end
|
end
|
||||||
|
@ -252,16 +247,23 @@ local function fix_alignment (l, start_i, stop_i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function replace_seperator_info (l, replace_with, indent_char, depth, start_i, stop_i)
|
||||||
|
for i = start_i or 1, stop_i or #l do
|
||||||
|
if type(l[i]) ~= 'table' then
|
||||||
|
-- Do nothing
|
||||||
|
elseif l[i][1] == 'seperator' then
|
||||||
|
l[i] = replace_with .. indent_char:rep(depth)
|
||||||
|
elseif l[i][1] == 'indent' then
|
||||||
|
l[i], depth = '', depth + 1
|
||||||
|
elseif l[i][1] == 'unindent' then
|
||||||
|
l[i], depth = '', depth - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Identifyer stuff
|
-- Identifyer stuff
|
||||||
|
|
||||||
local SIMPLE_VALUE_TYPES = {
|
|
||||||
['nil'] = true,
|
|
||||||
['boolean'] = true,
|
|
||||||
['number'] = true,
|
|
||||||
['string'] = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function is_empty_table (value)
|
local function is_empty_table (value)
|
||||||
if type(value) ~= 'table' then
|
if type(value) ~= 'table' then
|
||||||
error(('[pretty/internal]: Only tables allowed in function pretty.is_empty_table, but was given %s (%s)'):format(value, type(value)), 2)
|
error(('[pretty/internal]: Only tables allowed in function pretty.is_empty_table, but was given %s (%s)'):format(value, type(value)), 2)
|
||||||
|
@ -269,38 +271,6 @@ local function is_empty_table (value)
|
||||||
return next(value) == nil
|
return next(value) == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_short_table (value)
|
|
||||||
-- In this context, a short table is either an empty table, or one with a
|
|
||||||
-- single element.
|
|
||||||
|
|
||||||
if type(value) ~= 'table' then
|
|
||||||
error(('[pretty/internal]: Only tables allowed in function pretty.is_short_table, but was given %s (%s)'):format(value, type(value)), 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local first_key = next(value)
|
|
||||||
return (not first_key or SIMPLE_VALUE_TYPES[type(value[first_key])])
|
|
||||||
and (next(value, first_key) == nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_simple_value (value)
|
|
||||||
-- In this context, a simple value is a either nil, a boolean, a number,
|
|
||||||
-- a string or a short table.
|
|
||||||
-- TODO: Add clause about long strings. (Maybe >7 chars?)
|
|
||||||
|
|
||||||
--if type(value) == 'table' then print(value, is_short_table(value)) end
|
|
||||||
return SIMPLE_VALUE_TYPES[ type(value) ]
|
|
||||||
or type(value) == 'table' and is_short_table(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function contains_non_simple_key_or_value (t)
|
|
||||||
for k, v in pairs(t) do
|
|
||||||
if not is_simple_value(k) or not is_simple_value(v) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_table_type (value)
|
local function get_table_type (value)
|
||||||
-- Determines table type:
|
-- Determines table type:
|
||||||
-- * Sequence: All keys are integer in the range 1..#value
|
-- * Sequence: All keys are integer in the range 1..#value
|
||||||
|
@ -321,21 +291,6 @@ local function get_table_type (value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_single_line_table (value)
|
|
||||||
-- In this context, a single-line table, is:
|
|
||||||
-- A) Either a sequence or a pure map.
|
|
||||||
-- B) Has no non-simple keys or values
|
|
||||||
-- C 1) If sequence, has at most SINGLE_LINE_SEQ_MAX_ELEMENTS elements.
|
|
||||||
-- C 2) If map, has at most SINGLE_LINE_MAP_MAX_ELEMENTS elements.
|
|
||||||
|
|
||||||
local table_type = get_table_type(value)
|
|
||||||
|
|
||||||
return not contains_non_simple_key_or_value(value)
|
|
||||||
and SINGLE_LINE_TABLE_TYPES[table_type]
|
|
||||||
and #value <= SINGLE_LINE_SEQ_MAX_ELEMENTS
|
|
||||||
and nr_elements_in_map(value) <= SINGLE_LINE_MAP_MAX_ELEMENTS
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- Formatting stuff
|
-- Formatting stuff
|
||||||
|
|
||||||
|
@ -353,7 +308,7 @@ end
|
||||||
local function format_key_and_value_arbitr_map (l, key, value, options, depth)
|
local function format_key_and_value_arbitr_map (l, key, value, options, depth)
|
||||||
local index_before_key = #l+1
|
local index_before_key = #l+1
|
||||||
l[#l+1] = '['
|
l[#l+1] = '['
|
||||||
format_value(key, options, 'max', l)
|
format_value(key, options, 'max', l) -- TODO: Outphase the usage of the "max" depth thingy.
|
||||||
l[#l+1] = ']'
|
l[#l+1] = ']'
|
||||||
l[#l+1] = { width_of_strings_in_l(l, index_before_key), 'key' }
|
l[#l+1] = { width_of_strings_in_l(l, index_before_key), 'key' }
|
||||||
l[#l+1] = ' = '
|
l[#l+1] = ' = '
|
||||||
|
@ -375,7 +330,7 @@ local TABLE_TYPE_TO_PAIR_FORMAT = {
|
||||||
|
|
||||||
-- Formatting tables
|
-- Formatting tables
|
||||||
|
|
||||||
local function format_single_line_map (t, options, l)
|
local function format_map (t, options, depth, l)
|
||||||
-- NOTE: Assumes that the input table was pre-checked with `is_single_line_table()`
|
-- NOTE: Assumes that the input table was pre-checked with `is_single_line_table()`
|
||||||
|
|
||||||
local table_type = l.info[t] and l.info[t].type or get_table_type(t) -- FIXME: This is a temp fix
|
local table_type = l.info[t] and l.info[t].type or get_table_type(t) -- FIXME: This is a temp fix
|
||||||
|
@ -385,48 +340,33 @@ local function format_single_line_map (t, options, l)
|
||||||
end
|
end
|
||||||
local pair_format_func = TABLE_TYPE_TO_PAIR_FORMAT[table_type]
|
local pair_format_func = TABLE_TYPE_TO_PAIR_FORMAT[table_type]
|
||||||
|
|
||||||
l[#l+1] = '{ '
|
local start_of_table_i = #l + 1
|
||||||
local top_before = #l
|
l[#l+1] = '{'
|
||||||
|
l[#l+1] = {'indent'}
|
||||||
|
l[#l+1] = {'seperator'}
|
||||||
|
|
||||||
for _, pair in ipairs(key_value_pairs) do
|
for _, pair in ipairs(key_value_pairs) do
|
||||||
pair_format_func(l, pair[1], pair[2], options, 'max')
|
|
||||||
l[#l+1] = ', '
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ignore the "width of key"-shit
|
|
||||||
ignore_alignment_info(l, top_before, #l)
|
|
||||||
|
|
||||||
if l[#l] == ', ' then l[#l] = nil end
|
|
||||||
l[#l+1] = ' }'
|
|
||||||
end
|
|
||||||
|
|
||||||
local function format_map (t, options, depth, l)
|
|
||||||
-- TODO: l.
|
|
||||||
|
|
||||||
local table_type = l.info[t] and l.info[t].type or get_table_type(t) -- FIXME: This is a temp fix
|
|
||||||
local key_value_pairs = get_key_value_pairs_in_proper_order(t)
|
|
||||||
if table_type == TABLE_TYPE.SEQUENCE and l.info[t].has_holes then
|
|
||||||
fill_holes_in_key_value_pairs(key_value_pairs)
|
|
||||||
end
|
|
||||||
local pair_format_func = TABLE_TYPE_TO_PAIR_FORMAT[table_type]
|
|
||||||
|
|
||||||
l[#l+1] = '{\n'
|
|
||||||
local top_before = #l
|
|
||||||
for _, pair in pairs(key_value_pairs) do
|
|
||||||
l[#l+1] = options.indent:rep(depth + 1)
|
|
||||||
pair_format_func(l, pair[1], pair[2], options, depth + 1)
|
pair_format_func(l, pair[1], pair[2], options, depth + 1)
|
||||||
l[#l+1] = ',\n'
|
l[#l+1] = ','
|
||||||
|
l[#l+1] = {'seperator'}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fix whitespace alignment
|
if l[#l][1] == 'seperator' then l[#l-1], l[#l] = nil, nil end
|
||||||
fix_alignment(l, top_before, #l)
|
l[#l+1] = {'unindent'}
|
||||||
|
l[#l+1] = {'seperator'}
|
||||||
-- Fix and cleanup
|
|
||||||
if l[#l] == ',\n' then l[#l] = nil end
|
|
||||||
l[#l+1] = '\n'
|
|
||||||
l[#l+1] = options.indent:rep(depth)
|
|
||||||
l[#l+1] = '}'
|
l[#l+1] = '}'
|
||||||
|
|
||||||
|
local table_width = width_of_strings_in_l(l, start_of_table_i)
|
||||||
|
|
||||||
|
if table_width <= MAX_WIDTH_FOR_SINGLE_LINE_TABLE then
|
||||||
|
-- Is short table: Ignore the "width of key"-shit
|
||||||
|
replace_seperator_info(l, ' ', '', 0, start_of_table_i)
|
||||||
|
ignore_alignment_info(l, start_of_table_i)
|
||||||
|
else
|
||||||
|
-- Is long table: Fix whitespace alignment
|
||||||
|
replace_seperator_info(l, '\n', options.indent, depth, start_of_table_i)
|
||||||
|
fix_alignment(l, start_of_table_i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function format_table (t, options, depth, l)
|
function format_table (t, options, depth, l)
|
||||||
|
@ -444,9 +384,6 @@ function format_table (t, options, depth, l)
|
||||||
|
|
||||||
l.visited[t] = true
|
l.visited[t] = true
|
||||||
|
|
||||||
-- Single line?
|
|
||||||
if is_single_line_table(t) then return format_single_line_map(t, options, l) end
|
|
||||||
|
|
||||||
if depth == 'max' then l[#l+1] = '{...}'; return end
|
if depth == 'max' then l[#l+1] = '{...}'; return end
|
||||||
|
|
||||||
-- Normal table
|
-- Normal table
|
||||||
|
|
|
@ -84,7 +84,7 @@ do
|
||||||
adv_getlocal = true,
|
adv_getlocal = true,
|
||||||
input = { a = function () SOME_RANDOM_UPVALUE = true end },
|
input = { a = function () SOME_RANDOM_UPVALUE = true end },
|
||||||
options = { more_function_info = true },
|
options = { more_function_info = true },
|
||||||
expect = '{\n\ta = function () ... end\n}',
|
expect = '{ a = function () ... end }',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ format_test {
|
||||||
format_test {
|
format_test {
|
||||||
input = { math.cos, math.sin, math.abs },
|
input = { math.cos, math.sin, math.abs },
|
||||||
options = { short_builtins = true },
|
options = { short_builtins = true },
|
||||||
expect = '{\n\tmath.cos,\n\tmath.sin,\n\tmath.abs\n}',
|
expect = '{ math.cos, math.sin, math.abs }',
|
||||||
}
|
}
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
|
@ -217,7 +217,7 @@ format_test { -- Can include very small tables
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { {1, 2, 3}, {4, 5, 6} },
|
input = { {1, 2, 3}, {4, 5, 6} },
|
||||||
expect = '{\n\t{ 1, 2, 3 },\n\t{ 4, 5, 6 }\n}',
|
expect = '{ { 1, 2, 3 }, { 4, 5, 6 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
|
@ -232,17 +232,17 @@ format_test {
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { { {} } },
|
input = { { {} } },
|
||||||
expect = '{\n\t{ {} }\n}',
|
expect = '{ { {} } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { [{ 1, 2 }] = { 2, 1 } },
|
input = { [{ 1, 2 }] = { 2, 1 } },
|
||||||
expect = '{\n\t[{ 1, 2 }] = { 2, 1 }\n}',
|
expect = '{ [{ 1, 2 }] = { 2, 1 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
||||||
expect = '{\n\t{\n\t\t{ 1, 2 },\n\t\t{ 3, 4 }\n\t},\n\t{ 5, 6 }\n}',
|
expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
|
@ -254,24 +254,24 @@ format_test {
|
||||||
format_test {
|
format_test {
|
||||||
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
||||||
options = { max_depth = 1 },
|
options = { max_depth = 1 },
|
||||||
expect = '{\n\t{...},\n\t{...}\n}',
|
expect = '{ {...}, {...} }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
||||||
options = { max_depth = 2 },
|
options = { max_depth = 2 },
|
||||||
expect = '{\n\t{\n\t\t{...},\n\t\t{...}\n\t},\n\t{ 5, 6 }\n}',
|
expect = '{ { {...}, {...} }, { 5, 6 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
input = { { {1, 2}, {3, 4} }, {5, 6} },
|
||||||
options = { max_depth = 3 },
|
options = { max_depth = 3 },
|
||||||
expect = '{\n\t{\n\t\t{ 1, 2 },\n\t\t{ 3, 4 }\n\t},\n\t{ 5, 6 }\n}',
|
expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { [{ {1,2}, {3,4} }] = 'Hello World' },
|
input = { [{ {1,2}, {3,4} }] = 'Hello World' },
|
||||||
expect = '{\n\t[{...}] = \'Hello World\'\n}',
|
expect = '{ [{...}] = \'Hello World\' }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
|
@ -281,13 +281,13 @@ format_test {
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
input = { [true] = 1, [1] = false },
|
input = { [true] = 1, [1] = false },
|
||||||
expect = '{\n\t[1] = false,\n\t[true] = 1\n}',
|
expect = '{ [1] = false, [true] = 1 }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
-- Proper indent
|
-- Proper indent
|
||||||
input = { [1] = 1, ['whatever'] = false },
|
input = { [1] = 1, ['whatever'] = false },
|
||||||
expect = '{\n\t[1] = 1,\n\t[\'whatever\'] = false\n}',
|
expect = '{ [1] = 1, [\'whatever\'] = false }',
|
||||||
}
|
}
|
||||||
|
|
||||||
format_test {
|
format_test {
|
||||||
|
|
|
@ -46,9 +46,8 @@ end)
|
||||||
SUITE:addTest('no_std_lib', function ()
|
SUITE:addTest('no_std_lib', function ()
|
||||||
-- This tests whether one could load the library with an empty env, without
|
-- This tests whether one could load the library with an empty env, without
|
||||||
-- an error.
|
-- an error.
|
||||||
local chunk = loadfile('./library.lua')
|
local env = {}
|
||||||
setfenv(chunk, {})
|
local library = setfenv(loadfile('./library.lua'), env)()
|
||||||
local library = chunk()
|
|
||||||
|
|
||||||
for func, func_info in pairs(library) do
|
for func, func_info in pairs(library) do
|
||||||
error(('For some reason %s is defined in the library'):format(func_info.name))
|
error(('For some reason %s is defined in the library'):format(func_info.name))
|
||||||
|
@ -58,13 +57,22 @@ end)
|
||||||
SUITE:addTest('a_very_small_part_of_math', function ()
|
SUITE:addTest('a_very_small_part_of_math', function ()
|
||||||
-- This tests whether one could load the library with an empty env, without
|
-- This tests whether one could load the library with an empty env, without
|
||||||
-- an error.
|
-- an error.
|
||||||
local chunk = loadfile('./library.lua')
|
local env = { math = { abs = math.abs } }
|
||||||
setfenv(chunk, { math = { abs = math.abs } })
|
local library = setfenv(loadfile('./library.lua'), env)()
|
||||||
local library = chunk()
|
|
||||||
|
|
||||||
assert( library[math.abs], 'Why is math.abs not defined in the library?' )
|
assert( library[math.abs], 'Why is math.abs not defined in the library?' )
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('redefined part of math', function ()
|
||||||
|
-- This tests whether the library ignores a redefined function of an
|
||||||
|
-- builtin, when defining documentation.
|
||||||
|
local env = { setfenv = setfenv, loadfile = function() end }
|
||||||
|
local library = setfenv(loadfile('./library.lua'), env)()
|
||||||
|
|
||||||
|
assert( library[env.setfenv], 'Why is setfenv not defined in the library?' )
|
||||||
|
assert( not library[env.loadfile], 'Why is loadfile defined in the library?' )
|
||||||
|
end)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
return SUITE
|
return SUITE
|
||||||
|
|
Loading…
Reference in New Issue
Block a user