Skip to content

Commit 23fc5b9

Browse files
authored
Merge pull request #8 from lupodevelop/fix/1.2.1-grapheme-wrap-repeat
Fix/1.2.1 grapheme wrap repeat
2 parents d848392 + 50a15bf commit 23fc5b9

5 files changed

Lines changed: 27 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ All notable changes to this project are documented in this file.
1010
- Optimized `count/3` internals to avoid repeated `list.length` calls inside recursive loops, improving performance on long strings.
1111

1212

13+
Contributed by: Daniele (`lupodevelop`)
14+
15+
## [1.2.1] - 2026-01-02
16+
### Fixed
17+
- Made `repeat_str/2` iterative to avoid deep recursion and improve performance on large repetition counts.
18+
- Fixed `wrap_at/2` to measure word lengths using grapheme clusters to avoid splitting emoji and other multi-codepoint graphemes.
19+
- Replaced several `string.length(part) == 0` checks with `string.is_empty(part)` for clarity.
20+
21+
### Tests
22+
- Added `wrap_at_emoji_grapheme_test` to verify grapheme-aware wrapping behavior.
23+
24+
### Style
25+
- Ran `gleam format` to normalize formatting.
26+
1327
Contributed by: Daniele (`lupodevelop`)
1428

1529
## [1.1.1] - 2025-11-30

gleam.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "str"
2-
version = "1.2.0"
2+
version = "1.2.1"
33

44
# Project metadata (fill or replace placeholders before publishing)
55
description = "Unicode-aware string utilities for Gleam: grapheme-safe operations, pragmatic ASCII transliteration, and slug generation."

src/str/core.gleam

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ pub fn is_blank(text: String) -> Bool {
179179
fn repeat_str(s: String, n: Int) -> String {
180180
case n <= 0 {
181181
True -> ""
182-
False -> s <> repeat_str(s, n - 1)
182+
False -> list.fold(list.range(1, n), "", fn(acc, _) { acc <> s })
183183
}
184184
}
185185

@@ -693,7 +693,7 @@ fn wrap_words(
693693
case remaining {
694694
[] -> acc
695695
[word, ..rest] -> {
696-
let word_len = string.length(word)
696+
let word_len = string.to_graphemes(word) |> list.length
697697
case line_len == 0 {
698698
True ->
699699
// First word on line

src/str/extra.gleam

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ pub fn to_snake_case(s: String) -> String {
341341
pub fn to_camel_case(s: String) -> String {
342342
let parts = string.split(slugify(s), "-")
343343
list.fold(parts, "", fn(acc, part) {
344-
case string.length(part) == 0 {
344+
case string.is_empty(part) {
345345
True -> acc
346346
False ->
347347
case acc == "" {
@@ -364,7 +364,7 @@ pub fn to_camel_case(s: String) -> String {
364364
pub fn to_pascal_case(s: String) -> String {
365365
let parts = string.split(slugify(s), "-")
366366
list.fold(parts, "", fn(acc, part) {
367-
case string.length(part) == 0 {
367+
case string.is_empty(part) {
368368
True -> acc
369369
False ->
370370
acc
@@ -385,7 +385,7 @@ pub fn to_title_case(s: String) -> String {
385385
let parts = string.split(slugify(s), "-")
386386
let capitalized =
387387
list.map(parts, fn(part) {
388-
case string.length(part) == 0 {
388+
case string.is_empty(part) {
389389
True -> part
390390
False ->
391391
string.uppercase(string.slice(part, 0, 1))

test/str_core_test.gleam

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,13 @@ pub fn wrap_at_zero_width_test() {
388388
assert core.wrap_at("hello", 0) == "hello"
389389
}
390390

391+
pub fn wrap_at_emoji_grapheme_test() {
392+
// Ensure grapheme-aware wrapping treats emoji as single units
393+
let s = "a 👨‍👩‍👧‍👦 b"
394+
// Width 2 should force a newline between "a" and the emoji
395+
assert string.contains(core.wrap_at(s, 2), "\n")
396+
}
397+
391398
pub fn ellipsis_basic_test() {
392399
let result = core.ellipsis("Hello World", 8)
393400
assert string.ends_with(result, "…")

0 commit comments

Comments
 (0)