From 3baac1ab4b8f0766bfbd5b477de4a212c0f22b74 Mon Sep 17 00:00:00 2001 From: Daniele Date: Sat, 20 Dec 2025 17:11:01 +0100 Subject: [PATCH 1/4] perf(core): avoid repeated list.length in count_loop --- src/str/core.gleam | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/str/core.gleam b/src/str/core.gleam index 127a623..b8560ce 100644 --- a/src/str/core.gleam +++ b/src/str/core.gleam @@ -195,24 +195,51 @@ fn count_loop( overlapping: Bool, acc: Int, ) -> Int { - case list.length(hs) < nd_len { + count_loop_with_len(hs, list.length(hs), nd, nd_len, overlapping, acc) +} + +fn count_loop_with_len( + hs: List(String), + hs_len: Int, + nd: List(String), + nd_len: Int, + overlapping: Bool, + acc: Int, +) -> Int { + case hs_len < nd_len { True -> acc False -> case list.take(hs, nd_len) == nd { True -> case overlapping { True -> - count_loop(list.drop(hs, 1), nd, nd_len, overlapping, acc + 1) + count_loop_with_len( + list.drop(hs, 1), + hs_len - 1, + nd, + nd_len, + overlapping, + acc + 1, + ) False -> - count_loop( + count_loop_with_len( list.drop(hs, nd_len), + hs_len - nd_len, nd, nd_len, overlapping, acc + 1, ) } - False -> count_loop(list.drop(hs, 1), nd, nd_len, overlapping, acc) + False -> + count_loop_with_len( + list.drop(hs, 1), + hs_len - 1, + nd, + nd_len, + overlapping, + acc, + ) } } } From c973e949d2be2dad088aaaa942b0168e54f85425 Mon Sep 17 00:00:00 2001 From: Daniele Date: Sat, 20 Dec 2025 17:12:04 +0100 Subject: [PATCH 2/4] chore(ci): pin gleam and fix cache --- .github/workflows/ci.yml | 50 ++++++++++++++++++++++++++++++++++++++++ src/str/core.gleam | 7 +++--- 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c0c17dc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + name: Run tests + runs-on: ubuntu-latest + # Pin a specific Gleam version to avoid surprises from :latest + container: + image: gleamlang/gleam:1.13.0 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Restore _build cache + uses: actions/cache@v4 + with: + path: build + key: ${{ runner.os }}-gleam-build-${{ hashFiles('gleam.toml', 'manifest.toml') }} + + - name: Restore Gleam user cache + uses: actions/cache@v4 + with: + path: ~/.cache/gleam + key: ${{ runner.os }}-gleam-cache-${{ hashFiles('gleam.toml', 'manifest.toml') }} + + - name: Install dependencies (download) + run: gleam deps download + - name: Show environment + run: | + echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" + uname -a + + - name: Gleam version + run: gleam --version + + - name: Format check + run: | + # Check formatting; fail the job if files need formatting + gleam format --check + + - name: Run test suite + run: | + gleam test diff --git a/src/str/core.gleam b/src/str/core.gleam index b8560ce..4c395f5 100644 --- a/src/str/core.gleam +++ b/src/str/core.gleam @@ -12,7 +12,6 @@ import gleam/int import gleam/list import gleam/string -import str/tokenize /// Detects if a grapheme cluster likely contains emoji components. /// @@ -440,10 +439,10 @@ pub fn truncate_default(text: String, max_len: Int) -> String { /// reverse("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ") -> "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ" /// pub fn reverse(text: String) -> String { - let clusters = tokenize.chars(text) - clusters + text + |> string.to_graphemes |> list.reverse - |> list.fold("", fn(acc, s) { acc <> s }) + |> string.concat } // ============================================================================ From f2dfd3cf83cd310dc354e7e414324c55daaf9f41 Mon Sep 17 00:00:00 2001 From: Daniele Date: Sat, 20 Dec 2025 17:17:22 +0100 Subject: [PATCH 3/4] chore: bump version to 1.2.0 --- CHANGELOG.md | 12 ++++++++++++ gleam.toml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6c0f6b..00ac862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project are documented in this file. +## [1.2.0] - 2025-12-20 +### Changed +- `reverse/1` now uses the BEAM stdlib grapheme segmentation (`string.to_graphemes`) for better Unicode correctness and consistency across the library. + +### Performance +- Optimized `count/3` internals to avoid repeated `list.length` calls inside recursive loops, improving performance on long strings. + +### CI +- Pinned Gleam version in CI and fixed build cache path/key for more reproducible and faster runs. + +Contributed by: Daniele (`lupodevelop`) + ## [1.1.1] - 2025-11-30 ### Fixed - Robustness fixes for grapheme-aware utilities; resolved parity issues in `ends_with/2` for complex ZWJ sequences. diff --git a/gleam.toml b/gleam.toml index 773d060..dc062b1 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "str" -version = "1.1.1" +version = "1.2.0" # Project metadata (fill or replace placeholders before publishing) description = "Unicode-aware string utilities for Gleam: grapheme-safe operations, pragmatic ASCII transliteration, and slug generation." From 942896fa586c58e37e6f9c6af20e8e669de5800f Mon Sep 17 00:00:00 2001 From: Daniele Date: Sat, 20 Dec 2025 17:20:25 +0100 Subject: [PATCH 4/4] Remove CI workflow configuration Deleted the .github/workflows/ci.yml file, removing the continuous integration workflow for the project. This disables automated testing and formatting checks on push and pull request events. --- .github/workflows/ci.yml | 50 ---------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index c0c17dc..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: CI - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -jobs: - test: - name: Run tests - runs-on: ubuntu-latest - # Pin a specific Gleam version to avoid surprises from :latest - container: - image: gleamlang/gleam:1.13.0 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Restore _build cache - uses: actions/cache@v4 - with: - path: build - key: ${{ runner.os }}-gleam-build-${{ hashFiles('gleam.toml', 'manifest.toml') }} - - - name: Restore Gleam user cache - uses: actions/cache@v4 - with: - path: ~/.cache/gleam - key: ${{ runner.os }}-gleam-cache-${{ hashFiles('gleam.toml', 'manifest.toml') }} - - - name: Install dependencies (download) - run: gleam deps download - - name: Show environment - run: | - echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}" - uname -a - - - name: Gleam version - run: gleam --version - - - name: Format check - run: | - # Check formatting; fail the job if files need formatting - gleam format --check - - - name: Run test suite - run: | - gleam test