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 join(glue: Text, pieces: [Text] -> Text)`](#join)
- [`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 map(text: Text, pattern: Pattern, fn: func(text:Match)->Text -> Text, recursive: Bool = yes)`](#map)
- [`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_all(replacements:{Pattern,Text}, backref: Pattern = $/\/, recursive: Bool = yes -> Text)`](#replace_all)
- [`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 split(text: Text, pattern: Pattern = "" -> [Text])`](#split)
- [`func starts_with(text: Text, prefix: Text -> Bool)`](#starts_with)
- [`func title(text: Text, language: Text = "C" -> Text)`](#title)
- [`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`
Splits the text into an array of lines of text, preserving blank lines,
ignoring trailing newlines, and handling `\r\n` the same as `\n`.
```tomo
func split(text: Text -> [Text])
func lines(text: Text -> [Text])
```
- `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`
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)"},
{"has", "Text$has", "func(text:Text, pattern:Pattern -> Bool)"},
{"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])"},
{"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)"},
{"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)"},
{"repeat", "Text$repeat", "func(text:Text, count:Int -> 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)"},
{"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)"},
{"split", "Text$split", "func(text:Text, pattern=$Pattern'' -> [Text])"},
{"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;
}
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)
{
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);
Text_t Text$join(Text_t glue, Array_t pieces);
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);
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 *);

View File

@ -361,3 +361,16 @@ func main():
= 1
>> concat4 == final
= 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