From 36b2d05ff6bdf481130fcc37b11949d7973b6652 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 2 Jul 2021 13:21:54 -0700 Subject: [PATCH] add dfhack string functions and tests added string:split added string:trim added a default value for wrap width added function comments added tests for all string functions (the tests for string:split will be commented out until we remove the competing implementation in gui/load-screen --- library/lua/dfhack.lua | 36 +++++++++++++++++++++++++++++++++++- test/library/string.lua | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index bca2cc895..0742c23f5 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -361,19 +361,53 @@ function safe_index(obj,idx,...) end end +-- String class extentions + +-- prefix is a literal string, not a pattern function string:startswith(prefix) return self:sub(1, #prefix) == prefix end +-- suffix is a literal string, not a pattern function string:endswith(suffix) return self:sub(-#suffix) == suffix or #suffix == 0 end +-- Split a string by the given delimiter. If no delimiter is specified, space +-- (' ') is used. The delimter is treated as a pattern unless a is +-- specified and set to true. To treat multiple successive delimiter characters +-- as a single delimiter, e.g. to avoid getting empty string elements, pass a +-- pattern like ' +'. Be aware that passing patterns that match empty strings +-- (like ' *') will result in improper string splits. +function string:split(delimiter, plain) + delimiter = delimiter or ' ' + local result = {} + local from = 1 + local delim_from, delim_to = self:find(delimiter, from, plain) + -- delim_from will be greater than delim_to when the delimiter matches the + -- empty string, which would lead to an infinite loop if we didn't check it + while delim_from and delim_from <= delim_to do + table.insert(result, self:sub(from, delim_from - 1)) + from = delim_to + 1 + delim_from, delim_to = self:find(delimiter, from, plain) + end + table.insert(result, self:sub(from)) + return result +end + +-- Removes spaces (i.e. everything that matches '%s') from the start and end of +-- a string. Spaces between non-space characters are left untouched. +function string:trim() + local _, _, content = self:find('^%s*(.-)%s*$') + return content +end + -- Inserts newlines into a string so no individual line exceeds the given width. -- Lines are split at space-separated word boundaries. Any existing newlines are -- kept in place. If a single word is longer than width, it is split over --- multiple lines. +-- multiple lines. If width is not specified, 72 is used. function string:wrap(width) + width = width or 72 local wrapped_text = {} for line in self:gmatch('[^\n]*') do local line_start_pos = 1 diff --git a/test/library/string.lua b/test/library/string.lua index 58bd2d6e4..ffe4391a1 100644 --- a/test/library/string.lua +++ b/test/library/string.lua @@ -8,6 +8,9 @@ function test.startswith() expect.true_((''):startswith('')) expect.false_((''):startswith('a')) + + expect.false_(('str'):startswith('.'), + 'ensure we match literals, not patterns') end function test.endswith() @@ -18,6 +21,41 @@ function test.endswith() expect.true_((''):endswith('')) expect.false_((''):endswith('a')) + + expect.false_(('str'):endswith('.'), + 'ensure we match literals, not patterns') +end + +-- uncomment once gui/load-screen's string:split implementation is removed +--[[ +function test.split() + expect.table_eq({'hello','world'}, ('hello world'):split()) + expect.table_eq({'hello','','world'}, ('hello world'):split()) + expect.table_eq({'hello','world'}, ('hello world'):split(' +')) + + expect.table_eq({'hello','world'}, ('hello.world'):split('.', true), + 'ensure literal interpretation when plain is true') + + -- we don't actually care what this returns, just that it does return + expect.true_(('hello world'):split('.*'), 'ensure no infinite loop') + + expect.table_eq({'hello ', ' world'}, ('hello , world'):split(','), + 'ensure spaces are kept when they are not the delimiter') + expect.table_eq({'hello'}, ('hello'):split(), 'no delimiter') +end +]] + +function test.trim() + expect.eq('hello', ('hello'):trim()) + expect.eq('hello', (' hello'):trim()) + expect.eq('hello', ('hello '):trim()) + expect.eq('hello', (' hello '):trim()) + expect.eq('', (''):trim()) + expect.eq('', (' '):trim()) + expect.eq('', (' \t \n \v '):trim()) + expect.eq('hel lo', (' hel lo '):trim(), 'keep interior spaces') + expect.eq('hel \n lo', (' hel \n lo '):trim(), + 'keep interior spaces across newlines') end function test.wrap() @@ -29,5 +67,5 @@ function test.wrap() expect.eq('hello\nworld', ('hello world'):wrap(8)) expect.eq('hel\nlo\nwor\nld', ('hello world'):wrap(3)) expect.eq('hel\nloo\nwor\nldo', ('helloo worldo'):wrap(3)) - expect.eq('', (''):wrap(10)) + expect.eq('', (''):wrap()) end