aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-03-07 16:56:23 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-03-07 16:56:23 -0500
commit2ebe7893fe18c953967f602c73f6d3f32185eeb6 (patch)
tree6f2b5b83bc038907d8c387850ee3dd979bad2e71
parent9b485be020b6021f2cb86d97efb5b05166901bdf (diff)
Add text padding functions: :left_pad(), :right_pad(), :middle_pad()
-rw-r--r--docs/text.md85
-rw-r--r--environment.c3
-rw-r--r--stdlib/text.c41
-rw-r--r--stdlib/text.h3
-rw-r--r--test/text.tm13
5 files changed, 143 insertions, 2 deletions
diff --git a/docs/text.md b/docs/text.md
index cedc8d53..79fc5dd5 100644
--- a/docs/text.md
+++ b/docs/text.md
@@ -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.
diff --git a/environment.c b/environment.c
index f283e693..259cdd74 100644
--- a/environment.c
+++ b/environment.c
@@ -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)"},
diff --git a/stdlib/text.c b/stdlib/text.c
index c8700739..adbac3f0 100644
--- a/stdlib/text.c
+++ b/stdlib/text.c
@@ -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);
diff --git a/stdlib/text.h b/stdlib/text.h
index d3aba3f3..9923403c 100644
--- a/stdlib/text.h
+++ b/stdlib/text.h
@@ -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 *);
diff --git a/test/text.tm b/test/text.tm
index a1b36d97..91edddb4 100644
--- a/test/text.tm
+++ b/test/text.tm
@@ -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