feat: add trim, trimEnd, trimStart methods with optional chars argument to String class
Implement six new trimming methods on String: trim(), trim(chars), trimEnd(), trimEnd(chars), trimStart(), trimStart(chars). The core logic uses a private trim_ helper that accepts a chars string, converts it to code points, and iterates from the start and/or end of the string to remove matching characters. Default whitespace set is tab, carriage return, newline, and space. Includes comprehensive test coverage for default and custom character trimming, edge cases (empty strings, all-trimmed strings, 8-bit clean data), and runtime error when chars argument is not a string.
This commit is contained in:
@@ -236,6 +236,48 @@ class String is Sequence {
|
||||
return result
|
||||
}
|
||||
|
||||
trim() { trim_("\t\r\n ", true, true) }
|
||||
trim(chars) { trim_(chars, true, true) }
|
||||
trimEnd() { trim_("\t\r\n ", false, true) }
|
||||
trimEnd(chars) { trim_(chars, false, true) }
|
||||
trimStart() { trim_("\t\r\n ", true, false) }
|
||||
trimStart(chars) { trim_(chars, true, false) }
|
||||
|
||||
trim_(chars, trimStart, trimEnd) {
|
||||
if (!(chars is String)) {
|
||||
Fiber.abort("Characters must be a string.")
|
||||
}
|
||||
|
||||
var codePoints = chars.codePoints.toList
|
||||
|
||||
var start
|
||||
if (trimStart) {
|
||||
while (start = iterate(start)) {
|
||||
if (!codePoints.contains(codePointAt_(start))) break
|
||||
}
|
||||
|
||||
if (start == false) return ""
|
||||
} else {
|
||||
start = 0
|
||||
}
|
||||
|
||||
var end
|
||||
if (trimEnd) {
|
||||
end = byteCount_ - 1
|
||||
while (end >= start) {
|
||||
var codePoint = codePointAt_(end)
|
||||
if (codePoint != -1 && !codePoints.contains(codePoint)) break
|
||||
end = end - 1
|
||||
}
|
||||
|
||||
if (end < start) return ""
|
||||
} else {
|
||||
end = -1
|
||||
}
|
||||
|
||||
return this[start..end]
|
||||
}
|
||||
|
||||
*(count) {
|
||||
if (!(count is Num) || !count.isInteger || count < 0) {
|
||||
Fiber.abort("Count must be a non-negative integer.")
|
||||
|
||||
@@ -238,6 +238,56 @@ static const char* coreModuleSource =
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" trim() { trim_(\"\t\r\n \", true, true) }\n"
|
||||
"\n"
|
||||
" trim(chars) { trim_(chars, true, true) }\n"
|
||||
"\n"
|
||||
" trimEnd() { trim_(\"\t\r\n \", false, true) }\n"
|
||||
"\n"
|
||||
" trimEnd(chars) { trim_(chars, false, true) }\n"
|
||||
"\n"
|
||||
" trimStart() { trim_(\"\t\r\n \", true, false) }\n"
|
||||
"\n"
|
||||
" trimStart(chars) { trim_(chars, true, false) }\n"
|
||||
"\n"
|
||||
" trim_(chars, trimStart, trimEnd) {\n"
|
||||
" if (!(chars is String)) {\n"
|
||||
" Fiber.abort(\"Characters must be a string.\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var codePoints = chars.codePoints.toList\n"
|
||||
"// System.print(\"code points %(codePoints)\")\n"
|
||||
"\n"
|
||||
" var start\n"
|
||||
" if (trimStart) {\n"
|
||||
" while (start = iterate(start)) {\n"
|
||||
" if (!codePoints.contains(codePointAt_(start))) break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" if (start == false) return \"\"\n"
|
||||
" } else {\n"
|
||||
" start = 0\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var end\n"
|
||||
" if (trimEnd) {\n"
|
||||
" end = byteCount_ - 1\n"
|
||||
" while (end >= start) {\n"
|
||||
" var codePoint = codePointAt_(end)\n"
|
||||
"// System.print(\"test %(end) : %(codePoint)\")\n"
|
||||
" if (codePoint != -1 && !codePoints.contains(codePoint)) break\n"
|
||||
" end = end - 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
"// System.print(\"range %(start) %(end)\")\n"
|
||||
" if (end < start) return \"\"\n"
|
||||
" } else {\n"
|
||||
" end = -1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return this[start..end]\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" *(count) {\n"
|
||||
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
|
||||
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
|
||||
|
||||
Reference in New Issue
Block a user