Add text padding functions: :left_pad(), :right_pad(), :middle_pad()

This commit is contained in:
Bruce Hill 2025-03-07 16:56:23 -05:00
parent 9b485be020
commit 2ebe7893fe
5 changed files with 143 additions and 2 deletions

View File

@ -290,6 +290,9 @@ pattern documentation](patterns.md) for more details.
- [`func has(text: Text, pattern: Pattern -> Bool)`](#has) - [`func has(text: Text, pattern: Pattern -> Bool)`](#has)
- [`func join(glue: Text, pieces: [Text] -> Text)`](#join) - [`func join(glue: Text, pieces: [Text] -> Text)`](#join)
- [`func split(text: Text -> [Text])`](#lines) - [`func split(text: Text -> [Text])`](#lines)
- [`func middle_pad(text: Text, width: Int, pad: Text = " " -> Text)`](#middle_pad)
- [`func left_pad(text: Text, width: Int, pad: Text = " " -> Text)`](#left_pad)
- [`func lines(text: Text, pattern: Pattern = "" -> [Text])`](#lines)
- [`func lower(text: Text, language: Text = "C" -> Text)`](#lower) - [`func lower(text: Text, language: Text = "C" -> Text)`](#lower)
- [`func map(text: Text, pattern: Pattern, fn: func(text:Match)->Text -> Text, recursive: Bool = yes)`](#map) - [`func map(text: Text, pattern: Pattern, fn: func(text:Match)->Text -> Text, recursive: Bool = yes)`](#map)
- [`func matches(text: Text, pattern: Pattern -> [Text])`](#matches) - [`func matches(text: Text, pattern: Pattern -> [Text])`](#matches)
@ -298,8 +301,8 @@ pattern documentation](patterns.md) for more details.
- [`func replace(text: Text, pattern: Pattern, replacement: Text, backref: Pattern = $/\/, recursive: Bool = yes -> Text)`](#replace) - [`func replace(text: Text, pattern: Pattern, replacement: Text, backref: Pattern = $/\/, recursive: Bool = yes -> Text)`](#replace)
- [`func replace_all(replacements:{Pattern,Text}, backref: Pattern = $/\/, recursive: Bool = yes -> Text)`](#replace_all) - [`func replace_all(replacements:{Pattern,Text}, backref: Pattern = $/\/, recursive: Bool = yes -> Text)`](#replace_all)
- [`func reversed(text: Text -> Text)`](#reversed) - [`func reversed(text: Text -> Text)`](#reversed)
- [`func right_pad(text: Text, width: Int, pad: Text = " " -> Text)`](#right_pad)
- [`func slice(text: Text, from: Int = 1, to: Int = -1 -> Text)`](#slice) - [`func slice(text: Text, from: Int = 1, to: Int = -1 -> Text)`](#slice)
- [`func split(text: Text, pattern: Pattern = "" -> [Text])`](#split)
- [`func starts_with(text: Text, prefix: Text -> Bool)`](#starts_with) - [`func starts_with(text: Text, prefix: Text -> Bool)`](#starts_with)
- [`func title(text: Text, language: Text = "C" -> Text)`](#title) - [`func title(text: Text, language: Text = "C" -> Text)`](#title)
- [`func to(text: Text, last: Int -> Text)`](#to) - [`func to(text: Text, last: Int -> Text)`](#to)
@ -777,12 +780,64 @@ A single `Text` value with the pieces joined by the glue.
--- ---
### `middle_pad`
Pad some text on the left and right side so it reaches a target width.
```tomo
func middle_pad(text: Text, width: Int, pad: Text = " " -> Text)
```
- `text`: The text to pad.
- `width`: The target width.
- `pad`: The padding text (default: `" "`).
**Returns:**
Text with length at least `width`, with extra padding on the left and right as
needed. If `pad` has length greater than 1, it may be partially repeated to
reach the exact desired length.
**Example:**
```tomo
>> "x":middle_pad(6)
= " x "
>> "x":middle_pad(10, "ABC")
= "ABCAxABCAB"
```
---
### `left_pad`
Pad some text on the left side so it reaches a target width.
```tomo
func left_pad(text: Text, width: Int, pad: Text = " " -> Text)
```
- `text`: The text to pad.
- `width`: The target width.
- `pad`: The padding text (default: `" "`).
**Returns:**
Text with length at least `width`, with extra padding on the left as needed. If
`pad` has length greater than 1, it may be partially repeated to reach the
exact desired length.
**Example:**
```tomo
>> "x":left_pad(5)
= " x"
>> "x":left_pad(5, "ABC")
= "ABCAx"
```
---
### `lines` ### `lines`
Splits the text into an array of lines of text, preserving blank lines, Splits the text into an array of lines of text, preserving blank lines,
ignoring trailing newlines, and handling `\r\n` the same as `\n`. ignoring trailing newlines, and handling `\r\n` the same as `\n`.
```tomo ```tomo
func split(text: Text -> [Text]) func lines(text: Text -> [Text])
``` ```
- `text`: The text to be split into lines. - `text`: The text to be split into lines.
@ -1052,6 +1107,32 @@ A reversed version of the text.
--- ---
### `right_pad`
Pad some text on the right side so it reaches a target width.
```tomo
func right_pad(text: Text, width: Int, pad: Text = " " -> Text)
```
- `text`: The text to pad.
- `width`: The target width.
- `pad`: The padding text (default: `" "`).
**Returns:**
Text with length at least `width`, with extra padding on the right as needed. If
`pad` has length greater than 1, it may be partially repeated to reach the
exact desired length.
**Example:**
```tomo
>> "x":right_pad(5)
= "x "
>> "x":right_pad(5, "ABC")
= "xABCA"
```
---
### `slice` ### `slice`
Get a slice of the text. Get a slice of the text.

View File

@ -409,15 +409,18 @@ env_t *new_compilation_unit(CORD libname)
{"without_escaping", "Path$cleanup", "func(text:Text -> Path)"}, {"without_escaping", "Path$cleanup", "func(text:Text -> Path)"},
{"has", "Text$has", "func(text:Text, pattern:Pattern -> Bool)"}, {"has", "Text$has", "func(text:Text, pattern:Pattern -> Bool)"},
{"join", "Text$join", "func(glue:Text, pieces:[Text] -> Text)"}, {"join", "Text$join", "func(glue:Text, pieces:[Text] -> Text)"},
{"left_pad", "Text$left_pad", "func(text:Text, count:Int, pad=\" \" -> Text)"},
{"lines", "Text$lines", "func(text:Text -> [Text])"}, {"lines", "Text$lines", "func(text:Text -> [Text])"},
{"lower", "Text$lower", "func(text:Text, language=\"C\" -> Text)"}, {"lower", "Text$lower", "func(text:Text, language=\"C\" -> Text)"},
{"map", "Text$map", "func(text:Text, pattern:Pattern, fn:func(match:Match -> Text), recursive=yes -> Text)"}, {"map", "Text$map", "func(text:Text, pattern:Pattern, fn:func(match:Match -> Text), recursive=yes -> Text)"},
{"matches", "Text$matches", "func(text:Text, pattern:Pattern -> [Text]?)"}, {"matches", "Text$matches", "func(text:Text, pattern:Pattern -> [Text]?)"},
{"middle_pad", "Text$middle_pad", "func(text:Text, count:Int, pad=\" \" -> Text)"},
{"quoted", "Text$quoted", "func(text:Text, color=no -> Text)"}, {"quoted", "Text$quoted", "func(text:Text, color=no -> Text)"},
{"repeat", "Text$repeat", "func(text:Text, count:Int -> Text)"}, {"repeat", "Text$repeat", "func(text:Text, count:Int -> Text)"},
{"replace", "Text$replace", "func(text:Text, pattern:Pattern, replacement:Text, backref=$/\\/, recursive=yes -> Text)"}, {"replace", "Text$replace", "func(text:Text, pattern:Pattern, replacement:Text, backref=$/\\/, recursive=yes -> Text)"},
{"replace_all", "Text$replace_all", "func(text:Text, replacements:{Pattern,Text}, backref=$/\\/, recursive=yes -> Text)"}, {"replace_all", "Text$replace_all", "func(text:Text, replacements:{Pattern,Text}, backref=$/\\/, recursive=yes -> Text)"},
{"reversed", "Text$reversed", "func(text:Text -> Text)"}, {"reversed", "Text$reversed", "func(text:Text -> Text)"},
{"right_pad", "Text$right_pad", "func(text:Text, count:Int, pad=\" \" -> Text)"},
{"slice", "Text$slice", "func(text:Text, from=1, to=-1 -> Text)"}, {"slice", "Text$slice", "func(text:Text, from=1, to=-1 -> Text)"},
{"split", "Text$split", "func(text:Text, pattern=$Pattern'' -> [Text])"}, {"split", "Text$split", "func(text:Text, pattern=$Pattern'' -> [Text])"},
{"starts_with", "Text$starts_with", "func(text,prefix:Text -> Bool)"}, {"starts_with", "Text$starts_with", "func(text,prefix:Text -> Bool)"},

View File

@ -512,6 +512,47 @@ public Text_t Text$repeat(Text_t text, Int_t count)
return ret; return ret;
} }
static Text_t Text$repeat_to_length(Text_t to_repeat, int64_t length)
{
if (length <= 0)
return EMPTY_TEXT;
Text_t repeated = EMPTY_TEXT;
while (repeated.length + to_repeat.length <= length)
repeated = concat2(repeated, to_repeat);
if (repeated.length < length)
repeated = concat2(repeated, Text$slice(to_repeat, I_small(1), I(length - repeated.length)));
assert(repeated.length == length);
return repeated;
}
public Text_t Text$left_pad(Text_t text, Int_t count, Text_t padding)
{
if (padding.length == 0)
fail("Cannot pad with an empty text!");
return concat2(Text$repeat_to_length(padding, Int64$from_int(count, false) - text.length), text);
}
public Text_t Text$right_pad(Text_t text, Int_t count, Text_t padding)
{
if (padding.length == 0)
fail("Cannot pad with an empty text!");
return concat2(text, Text$repeat_to_length(padding, Int64$from_int(count, false) - text.length));
}
public Text_t Text$middle_pad(Text_t text, Int_t count, Text_t padding)
{
if (padding.length == 0)
fail("Cannot pad with an empty text!");
int64_t needed = Int64$from_int(count, false) - text.length;
return Texts(Text$repeat_to_length(padding, needed/2), text, Text$repeat_to_length(padding, (needed+1)/2));
}
public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int)
{ {
int64_t first = Int64$from_int(first_int, false); int64_t first = Int64$from_int(first_int, false);

View File

@ -68,6 +68,9 @@ Array_t Text$lines(Text_t text);
Closure_t Text$by_line(Text_t text); Closure_t Text$by_line(Text_t text);
Text_t Text$join(Text_t glue, Array_t pieces); Text_t Text$join(Text_t glue, Array_t pieces);
Text_t Text$repeat(Text_t text, Int_t count); Text_t Text$repeat(Text_t text, Int_t count);
Text_t Text$left_pad(Text_t text, Int_t count, Text_t padding);
Text_t Text$right_pad(Text_t text, Int_t count, Text_t padding);
Text_t Text$middle_pad(Text_t text, Int_t count, Text_t padding);
int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index); int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index);
uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index); uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index);
void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);

View File

@ -361,3 +361,16 @@ func main():
= 1 = 1
>> concat4 == final >> concat4 == final
= yes = yes
>> "x":left_pad(5)
= " x"
>> "x":right_pad(5)
= "x "
>> "x":middle_pad(5)
= " x "
>> "1234":left_pad(8, "XYZ")
= "XYZX1234" : Text
>> "1234":right_pad(8, "XYZ")
= "1234XYZX" : Text
>> "1234":middle_pad(9, "XYZ")
= "XY1234XYZ" : Text