sema-strings
0.1.0Case conversion, slugs, padding, wrapping, and string distance utilities
sema-strings
Case conversion, slugs, padding, wrapping, and string distance utilities.
Modeled on camel-snake-kebab,
s.el, and
cuerdas — but covering only what the
Sema stdlib lacks. The stdlib already ships string/camel-case,
string/kebab-case, string/snake-case, string/pascal-case,
string/capitalize, string/title-case, string/headline,
string/pad-left, string/pad-right, string/lines, string/words,
string/word-wrap, string/empty?, and string/number?; none of those are
duplicated here.
Install
sema pkg add sema-strings
Quick start
(import "sema-strings")
(str/constant "fooBar")
; => "FOO_BAR"
(str/slug "Blåbær og rømme!")
; => "blabaer-og-romme"
(str/truncate "hello world" 5)
; => "hell…"
(str/pluralize 3 "file")
; => "3 files"
Unicode
Lengths and slices count Unicode scalar values (codepoints), same as the
stdlib's string-length/substring: (string-length "æ") is 1, so
str/pad-center, str/truncate, str/wrap, and str/levenshtein treat "æ"
as one character. Grapheme clusters that span multiple codepoints (ZWJ emoji,
combining marks) count per codepoint, and display width may differ from
character count — for terminal column layout use the stdlib's string/width.
API
| Function | Description |
|---|---|
(str/constant s) |
"foo-bar" → "FOO_BAR" (CONSTANT_CASE) |
(str/human s) |
"foo-bar" → "Foo bar" (human sentence) |
(str/slug s) |
URL slug: lowercase, transliterated, hyphenated |
(str/pad-center s width char?) |
Center in a field of width characters |
(str/truncate s max-len opts?) |
Truncate with an omission marker, never exceeding max-len |
(str/wrap s width) |
Word-wrap to \n-joined lines; long words stay unbroken |
(str/levenshtein a b) |
Levenshtein edit distance in characters |
(str/blank? s) |
#t for nil, "", or whitespace-only |
(str/numeric? s) |
#t for one or more ASCII digits, nothing else |
(str/unlines lines) |
Join a list of strings with \n |
(str/ordinal n) |
1 → "1st", 12 → "12th" |
(str/pluralize n word plural?) |
"3 files" — naive count + noun formatting |
str/constant
(str/constant "foo-bar") ; => "FOO_BAR"
(str/constant "fooBar") ; => "FOO_BAR"
(str/constant "HTTPServer") ; => "HTTP_SERVER"
Tokenizes with the stdlib's string/words (splits on -, _, spaces, .,
and camelCase boundaries, including acronym runs), uppercases every word, and
joins with _.
str/human
(str/human "foo-bar") ; => "Foo bar"
(str/human "fooBarBaz") ; => "Foo bar baz"
(str/human "HTTPServer") ; => "Http server"
Capitalizes only the first word; acronyms are lowercased like any other word.
For Every Word Capitalized, use the stdlib's string/headline.
str/slug
(str/slug "Hello, World!") ; => "hello-world"
(str/slug "Blåbær og rømme") ; => "blabaer-og-romme"
(str/slug "Über Straße Café") ; => "uber-strasse-cafe"
(str/slug "Top 10 Lists") ; => "top-10-lists"
Lowercases, transliterates common accented Latin characters (æ→ae,
ø→o, å→a, é→e, ü→u, ß→ss, œ→oe, ñ→n, ...), replaces
every other non-alphanumeric run with a single hyphen, and trims
leading/trailing hyphens. Characters outside the transliteration table (CJK,
emoji) are dropped.
str/pad-center
(str/pad-center "hi" 6) ; => " hi "
(str/pad-center "hi" 5) ; => " hi " (extra padding goes right)
(str/pad-center "x" 5 "*") ; => "**x**"
(str/pad-center "hello" 3) ; => "hello" (already wide enough)
char defaults to " " and must be a single-character string. Complements
the stdlib's string/pad-left and string/pad-right.
str/truncate
(str/truncate "hello world" 5) ; => "hell…"
(str/truncate "hello world" 5 {:omission "..."}) ; => "he..."
(str/truncate "hi" 10) ; => "hi"
The omission (default "…") counts toward max-len, so the result never
exceeds it. If the omission alone is max-len or longer, a prefix of the
omission is returned ((str/truncate "hello" 2 {:omission "..."}) → "..").
str/wrap
(str/wrap "the quick brown fox" 10)
; => "the quick\nbrown fox"
(str/wrap "a supercalifragilistic word" 5)
; => "a\nsupercalifragilistic\nword"
Splits on whitespace runs and greedily fills lines of at most width
characters, joined with \n. A word longer than width is kept unbroken on
its own line (that line then exceeds width). Distinct from the stdlib's
string/word-wrap, which returns a list of lines and hard-breaks long words.
str/levenshtein
(str/levenshtein "kitten" "sitting") ; => 3
(str/levenshtein "cat" "cats") ; => 1
(str/levenshtein "blæ" "bla") ; => 1 (per character, not per byte)
Iterative two-row dynamic programming — linear memory, no recursion depth limits.
str/blank?
(str/blank? nil) ; => #t
(str/blank? "") ; => #t
(str/blank? " \t\n") ; => #t
(str/blank? " x ") ; => #f
Anything other than nil or a string raises an error.
str/numeric?
(str/numeric? "42") ; => #t
(str/numeric? "3.14") ; => #f
(str/numeric? "-2") ; => #f
(str/numeric? "") ; => #f
One or more ASCII digits 0-9, nothing else. Stricter than the stdlib's
string/number?, which accepts anything parseable as a number.
str/unlines
(str/unlines (list "a" "b" "c")) ; => "a\nb\nc"
(str/unlines (string/lines "a\nb\n")) ; => "a\nb"
Joins with \n — the inverse of the stdlib's string/lines (up to a trailing
newline, which string/lines drops).
str/ordinal
(str/ordinal 1) ; => "1st"
(str/ordinal 2) ; => "2nd"
(str/ordinal 12) ; => "12th"
(str/ordinal 21) ; => "21st"
English ordinal suffixes, with the 11/12/13 (and 111/112/113, ...) exceptions handled.
str/pluralize
(str/pluralize 1 "file") ; => "1 file"
(str/pluralize 3 "file") ; => "3 files"
(str/pluralize 2 "person" "people") ; => "2 people"
Deliberately naive: the default plural just appends "s" — pass an explicit
plural for irregular nouns. n = 1 selects the singular; everything else
(including 0) selects the plural.
Testing
sema pkg add sema-test # once
sema tests.sema
License
MIT
| Version | Size | Published |
|---|---|---|
| 0.1.0 | 8 KB | 2026-07-07 21:28:15 |