diff --git a/compiler/test/stdlib/string.test.gr b/compiler/test/stdlib/string.test.gr index d2ece19bd9..030921825b 100644 --- a/compiler/test/stdlib/string.test.gr +++ b/compiler/test/stdlib/string.test.gr @@ -258,3 +258,28 @@ assert String.decode(bytes, String.UTF16_LE) == "¢" }, emojis) assert Array.reverse(Array.fromList(tmp)) == Array.mapi((c,i) => (c,i), codes) } + +// String.trimStart +assert String.trimStart("t test") == "t test" +assert String.trimStart(" test") == "test" +assert String.trimStart(" test ") == "test " +assert String.trimStart("test ") == "test " +assert String.trimStart("test") == "test" +assert String.trimStart("") == "" +assert String.trimStart(" ") == "" +// Sting.trimEnd +assert String.trimEnd("test t") == "test t" +assert String.trimEnd("test ") == "test" +assert String.trimEnd(" test ") == " test" +assert String.trimEnd(" test") == " test" +assert String.trimEnd("test") == "test" +assert String.trimEnd("") == "" +assert String.trimEnd(" ") == "" +// String.trim +assert String.trim("t test t") == "t test t" +assert String.trim("test ") == "test" +assert String.trim(" test ") == "test" +assert String.trim(" test") == "test" +assert String.trim("test") == "test" +assert String.trim("") == "" +assert String.trim(" ") == "" diff --git a/stdlib/string.gr b/stdlib/string.gr index 695d81ecc6..3290a46833 100644 --- a/stdlib/string.gr +++ b/stdlib/string.gr @@ -1609,3 +1609,56 @@ export let rec forEachCodePointi = (fn: (Number, Number) -> Void, str: String) = Memory.decRef(WasmI32.fromGrain(forEachCodePointi)) void } +let trimString = (str: String, end: Bool) => { + let chars = explode(str), charsLength = length(str) + let mut i = 0, offset = 1 + if (end) { + i = charsLength-1 + offset = -1 + } + for (; i < charsLength && i > -1; i += offset) { + let currentChar = chars[i]; + // TODO: Use unicode whitespace property and unicode line terminator once github issue #661 is completed + if ( + // Spacing + currentChar != '\u{0009}' && // Tab + currentChar != '\u{000B}' && // LINE TABULATION + currentChar != '\u{000C}' && // FORM FEED (FF) + currentChar != '\u{0020}' && // Space + currentChar != '\u{00A0}' && // No Break Space + currentChar != '\u{FEFF}' && // ZERO WIDTH NO-BREAK SPACE + // Line Terminators + currentChar != '\n' && // LF + currentChar != '\r' // CR + ) break + } + if (end) slice(0, i+1, str) + else slice(i, charsLength, str) +} +/** + * Trims the beginning of a string—removing any leading whitespace characters. + * + * @param string: The string to be trimmed + * @returns The trimmed string + * + * @example String.trimStart(" Hello World") == "Hello World" + */ +export let trimStart = (string: String) => trimString(string, false) +/** + * Trims the end of a string—removing any trailing whitespace characters. + * + * @param string: The string to be trimmed + * @returns The trimmed string + * + * @example String.trimEnd("Hello World ") == "Hello World" + */ +export let trimEnd = (string: String) => trimString(string, true) +/** + * Trims a string—removing all leading and trailing whitespace characters. + * + * @param string: The string to be trimmed + * @returns The trimmed string + * + * @example String.trim(" Hello World ") == "Hello World" + */ +export let trim = (string: String) => trimEnd(trimStart(string))