Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Unreleased

- Fixed a bug where `string.drop_start` would return incorrect results on
JavaScript when the string contained multi-byte characters.

## v1.0.4 - 2026-05-30

- Fix a bug where dicts and sets with hash collisions but equal entries would
Expand Down
2 changes: 1 addition & 1 deletion src/gleam/string.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ pub fn slice(from string: String, at_index idx: Int, length len: Int) -> String
fn grapheme_slice(string: String, index: Int, length: Int) -> String

@external(erlang, "binary", "part")
@external(javascript, "../gleam_stdlib.mjs", "string_byte_slice")
fn unsafe_byte_slice(string: String, index: Int, length: Int) -> String

/// Drops contents of the first `String` that occur before the second `String`.
Expand All @@ -227,6 +226,7 @@ pub fn crop(from string: String, before substring: String) -> String
/// assert drop_start(from: "The Lone Gunmen", up_to: 2) == "e Lone Gunmen"
/// ```
///
@external(javascript, "../gleam_stdlib.mjs", "string_drop_start")
pub fn drop_start(from string: String, up_to num_graphemes: Int) -> String {
case num_graphemes <= 0 {
True -> string
Expand Down
9 changes: 7 additions & 2 deletions src/gleam_stdlib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,13 @@ export function length(data) {
return data.length;
}

export function string_byte_slice(string, index, length) {
return string.slice(index, index + length);
export function string_drop_start(string, num_graphemes) {
if (num_graphemes <= 0) {
return string;
}

const prefix = string_grapheme_slice(string, 0, num_graphemes);
Comment thread
jtdowney marked this conversation as resolved.
Outdated
return string.slice(prefix.length);
}

export function string_grapheme_slice(string, idx, len) {
Expand Down
13 changes: 13 additions & 0 deletions test/gleam/string_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,19 @@ pub fn drop_start_3499_test() {
assert string.drop_start("\r]", 1) == "]"
}

pub fn drop_start_multibyte_test() {
// https://github.com/gleam-lang/stdlib/issues/924
assert string.drop_start("广州abcdefghijklmn", 0) == "广州abcdefghijklmn"
assert string.drop_start("广州abcdefghijklmn", 1) == "州abcdefghijklmn"
assert string.drop_start("广州abcdefghijklmn", 2) == "abcdefghijklmn"
assert string.drop_start("广州abcdefghijklmn", 3) == "bcdefghijklmn"
}

pub fn drop_start_grapheme_cluster_test() {
assert string.drop_start("👶🏿abc", 1) == "abc"
assert string.drop_start("e\u{0301}abc", 1) == "abc"
}

pub fn drop_end_basic_test() {
assert string.drop_end("gleam", up_to: 2) == "gle"
}
Expand Down
Loading