From 7bb357f36e15252ea9f5615b731c7fc4370c598f Mon Sep 17 00:00:00 2001 From: Thijs Schreijer Date: Fri, 31 Dec 2021 15:15:44 +0100 Subject: chore(docs) update docs of the 'text' module --- lua/pl/text.lua | 206 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 114 insertions(+), 92 deletions(-) diff --git a/lua/pl/text.lua b/lua/pl/text.lua index e626e86..1ebdd29 100644 --- a/lua/pl/text.lua +++ b/lua/pl/text.lua @@ -6,15 +6,6 @@ -- -- See @{03-strings.md.String_Templates|the Guide}. -- --- Calling `text.format_operator()` overloads the % operator for strings to give Python/Ruby style formated output. --- This is extended to also do template-like substitution for map-like data. --- --- > require 'pl.text'.format_operator() --- > = '%s = %5.3f' % {'PI',math.pi} --- PI = 3.142 --- > = '$name = $value' % {name='dog',value='Pluto'} --- dog = Pluto --- -- Dependencies: `pl.utils`, `pl.types` -- @module pl.text @@ -24,6 +15,7 @@ local utils = require 'pl.utils' local bind1,usplit,assert_arg = utils.bind1,utils.split,utils.assert_arg local is_callable = require 'pl.types'.is_callable local unpack = utils.unpack +local pack = utils.pack local text = {} @@ -48,9 +40,9 @@ local function _indent (s,sp) end --- indent a multiline string. --- @param s the string --- @param n the size of the indent --- @param ch the character to use when indenting (default ' ') +-- @tparam string s the (multiline) string +-- @tparam integer n the size of the indent +-- @tparam[opt=' '] string ch the character to use when indenting -- @return indented string function text.indent (s,n,ch) assert_arg(1,s,'string') @@ -60,7 +52,7 @@ end --- dedent a multiline string by removing any initial indent. -- useful when working with [[..]] strings. --- @param s the string +-- @tparam string s the (multiline) string -- @return a string with initial indent zero. function text.dedent (s) assert_arg(1,s,'string') @@ -73,10 +65,11 @@ end --- format a paragraph into lines so that they fit into a line width. -- It will not break long words, so lines can be over the length -- to that extent. --- @param s the string --- @param width the margin width, default 70 --- @return a list of lines (List object) +-- @tparam string s the string to format +-- @tparam[opt=70] integer width the margin width +-- @return a list of lines (List object), use `fill` to return a string instead of a `List`. -- @see pl.List +-- @see fill function text.wrap (s,width) assert_arg(1,s,'string') width = width or 70 @@ -96,14 +89,103 @@ function text.wrap (s,width) end --- format a paragraph so that it fits into a line width. --- @param s the string --- @param width the margin width, default 70 --- @return a string +-- @tparam string s the string to format +-- @tparam[opt=70] integer width the margin width +-- @return a string, use `wrap` to return a list of lines instead of a string. -- @see wrap function text.fill (s,width) return concat(text.wrap(s,width),'\n') .. '\n' end + +local function _substitute(s,tbl,safe) + local subst + if is_callable(tbl) then + subst = tbl + else + function subst(f) + local s = tbl[f] + if not s then + if safe then + return f + else + error("not present in table "..f) + end + else + return s + end + end + end + local res = gsub(s,'%${([%w_]+)}',subst) + return (gsub(res,'%$([%w_]+)',subst)) +end + +--- Python-style formatting operator. +-- Calling `text.format_operator()` overloads the % operator for strings to give +-- Python/Ruby style formated output. +-- This is extended to also do template-like substitution for map-like data. +-- +-- Note this goes further than the original, and will allow these cases: +-- +-- 1. a single value +-- 2. a list of values +-- 3. a map of var=value pairs +-- 4. a function, as in gsub +-- +-- For the second two cases, it uses $-variable substituion. +-- +-- When called, this function will monkey-patch the global `string` metatable by +-- adding a `__mod` method. +-- +-- See the lua-users wiki +-- +-- @usage +-- require 'pl.text'.format_operator() +-- local out1 = '%s = %5.3f' % {'PI',math.pi} --> 'PI = 3.142' +-- local out2 = '$name = $value' % {name='dog',value='Pluto'} --> 'dog = Pluto' +function text.format_operator() + + local format = string.format + + -- a more forgiving version of string.format, which applies + -- tostring() to any value with a %s format. + local function formatx (fmt,...) + local args = pack(...) + local i = 1 + for p in fmt:gmatch('%%.') do + if p == '%s' and type(args[i]) ~= 'string' then + args[i] = tostring(args[i]) + end + i = i + 1 + end + return format(fmt,unpack(args)) + end + + local function basic_subst(s,t) + return (s:gsub('%$([%w_]+)',t)) + end + + getmetatable("").__mod = function(a, b) + if b == nil then + return a + elseif type(b) == "table" and getmetatable(b) == nil then + if #b == 0 then -- assume a map-like table + return _substitute(a,b,true) + else + return formatx(a,unpack(b)) + end + elseif type(b) == 'function' then + return basic_subst(a,b) + else + return formatx(a,b) + end + end +end + + +--- @section Template + + local Template = {} text.Template = Template Template.__index = Template @@ -112,6 +194,11 @@ setmetatable(Template, { return Template.new(tmpl) end}) +--- Creates a new Template class. +-- This is a shortcut to `Template.new(tmpl)`. +-- @tparam string tmpl the template string +-- @function Template +-- @treturn Template function Template.new(tmpl) assert_arg(1,tmpl,'string') local res = {} @@ -120,31 +207,10 @@ function Template.new(tmpl) return res end -local function _substitute(s,tbl,safe) - local subst - if is_callable(tbl) then - subst = tbl - else - function subst(f) - local s = tbl[f] - if not s then - if safe then - return f - else - error("not present in table "..f) - end - else - return s - end - end - end - local res = gsub(s,'%${([%w_]+)}',subst) - return (gsub(res,'%$([%w_]+)',subst)) -end - --- substitute values into a template, throwing an error. -- This will throw an error if no name is found. --- @param tbl a table of name-value pairs. +-- @tparam table tbl a table of name-value pairs. +-- @return string with place holders substituted function Template:substitute(tbl) assert_arg(1,tbl,'table') return _substitute(self.tmpl,tbl,false) @@ -152,7 +218,8 @@ end --- substitute values into a template. -- This version just passes unknown names through. --- @param tbl a table of name-value pairs. +-- @tparam table tbl a table of name-value pairs. +-- @return string with place holders substituted function Template:safe_substitute(tbl) assert_arg(1,tbl,'table') return _substitute(self.tmpl,tbl,true) @@ -161,9 +228,10 @@ end --- substitute values into a template, preserving indentation.
-- If the value is a multiline string _or_ a template, it will insert -- the lines at the correct indentation.
--- Furthermore, if a template, then that template will be subsituted +-- Furthermore, if a template, then that template will be substituted -- using the same table. --- @param tbl a table of name-value pairs. +-- @tparam table tbl a table of name-value pairs. +-- @return string with place holders substituted function Template:indent_substitute(tbl) assert_arg(1,tbl,'table') if not self.strings then @@ -191,56 +259,10 @@ function Template:indent_substitute(tbl) end end) end + local lines = imap(subst,self.strings) return concat(lines,'\n')..'\n' end -------- Python-style formatting operator ------ --- (see the lua-users wiki) -- - -function text.format_operator() - - local format = string.format - - -- a more forgiving version of string.format, which applies - -- tostring() to any value with a %s format. - local function formatx (fmt,...) - local args = {...} - local i = 1 - for p in fmt:gmatch('%%.') do - if p == '%s' and type(args[i]) ~= 'string' then - args[i] = tostring(args[i]) - end - i = i + 1 - end - return format(fmt,unpack(args)) - end - - local function basic_subst(s,t) - return (s:gsub('%$([%w_]+)',t)) - end - - -- Note this goes further than the original, and will allow these cases: - -- 1. a single value - -- 2. a list of values - -- 3. a map of var=value pairs - -- 4. a function, as in gsub - -- For the second two cases, it uses $-variable substituion. - getmetatable("").__mod = function(a, b) - if b == nil then - return a - elseif type(b) == "table" and getmetatable(b) == nil then - if #b == 0 then -- assume a map-like table - return _substitute(a,b,true) - else - return formatx(a,unpack(b)) - end - elseif type(b) == 'function' then - return basic_subst(a,b) - else - return formatx(a,b) - end - end -end return text -- cgit v1.2.3