diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index db5040bdd..abc466a14 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +* [JS/TS] Add support for `OrdinalIgnoreCase` overload for `String.EndsWith` (#3892) (by @goswinr) + ### Changed * [Python] Remove `$` sign when reporting an error from `assert_equal` and `assert_not_equal` (#3878) (by @joprice) diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index efce42c56..f281afb38 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -1449,7 +1449,6 @@ let implementedStringFunctions = [| "Compare" "CompareTo" - "EndsWith" "Format" "IndexOfAny" "Insert" @@ -1503,12 +1502,15 @@ let strings (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr opt let left = Helper.InstanceCall(c, "indexOf", Int32.Number, [ arg ]) makeEqOp r left (makeIntConst 0) BinaryGreaterOrEqual |> Some - | "StartsWith", Some c, [ _str ] -> - let left = Helper.InstanceCall(c, "indexOf", Int32.Number, args) - makeEqOp r left (makeIntConst 0) BinaryEqual |> Some + | "StartsWith", Some c, [ _str ] -> Helper.InstanceCall(c, "startsWith", Boolean, args) |> Some | "StartsWith", Some c, [ _str; _comp ] -> Helper.LibCall(com, "String", "startsWith", t, args, i.SignatureArgTypes, thisArg = c, ?loc = r) |> Some + | "EndsWith", Some c, [ _str ] -> Helper.InstanceCall(c, "endsWith", Boolean, args) |> Some + | "EndsWith", Some c, [ _str; _comp ] -> + Helper.LibCall(com, "String", "endsWith", t, args, i.SignatureArgTypes, thisArg = c, ?loc = r) + |> Some + | ReplaceName [ "ToUpper", "toLocaleUpperCase" "ToUpperInvariant", "toUpperCase" "ToLower", "toLocaleLowerCase" diff --git a/src/fable-library-ts/CHANGELOG.md b/src/fable-library-ts/CHANGELOG.md index c9f5ec329..e82a31c94 100644 --- a/src/fable-library-ts/CHANGELOG.md +++ b/src/fable-library-ts/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +* [JS/TS] Add support for `OrdinalIgnoreCase` overload for `String.EndsWith` (#3892) (by @goswinr) + ### Fixed * [JS/TS] Fix escaping of `{` and `}` in FormattableString (#3890) (by @roboz0r) diff --git a/src/fable-library-ts/String.ts b/src/fable-library-ts/String.ts index 955cf0697..6b58e844b 100644 --- a/src/fable-library-ts/String.ts +++ b/src/fable-library-ts/String.ts @@ -64,12 +64,25 @@ export function compareTo(x: string, y: string) { } export function startsWith(str: string, pattern: string, ic: number) { + if (ic === StringComparison.Ordinal) { // to avoid substring allocation + return str.startsWith(pattern); + } if (str.length >= pattern.length) { return cmp(str.substr(0, pattern.length), pattern, ic) === 0; } return false; } +export function endsWith(str: string, pattern: string, ic: number) { + if (ic === StringComparison.Ordinal) { // to avoid substring allocation + return str.endsWith(pattern); + } + if (str.length >= pattern.length) { + return cmp(str.substr(str.length - pattern.length, pattern.length), pattern, ic) === 0; + } + return false; +} + export function indexOfAny(str: string, anyOf: string[], ...args: number[]) { if (str == null || str === "") { return -1; @@ -374,10 +387,6 @@ export function format(str: string | object, ...args: any[]) { }); } -export function endsWith(str: string, search: string) { - const idx = str.lastIndexOf(search); - return idx >= 0 && idx === str.length - search.length; -} export function initialize(n: number, f: (i: number) => string) { if (n < 0) { diff --git a/tests/Js/Main/StringTests.fs b/tests/Js/Main/StringTests.fs index 31a63960c..bda0f73e7 100644 --- a/tests/Js/Main/StringTests.fs +++ b/tests/Js/Main/StringTests.fs @@ -815,23 +815,28 @@ let tests = testList "Strings" [ "abcdbcebc".IndexOfAny([|'c';'b'|]) |> equal 1 testCase "String.StartsWith works" <| fun () -> - let args = [("ab", true); ("cd", false); ("abcdx", false)] + let args = [("ab", true); ("bc", false); ("cd", false); ("abcdx", false); ("abcd", true)] for arg in args do "abcd".StartsWith(fst arg) |> equal (snd arg) - testCase "String.StartsWith with StringComparison works" <| fun () -> - let args = [("ab", true); ("cd", false); ("abcdx", false)] + testCase "String.StartsWith with OrdinalIgnoreCase works" <| fun () -> + let args = [("ab", true); ("AB", true); ("BC", false); ("cd", false); ("abcdx", false); ("abcd", true)] for arg in args do "ABCD".StartsWith(fst arg, StringComparison.OrdinalIgnoreCase) |> equal (snd arg) - testCase "String.EndsWith works" <| fun () -> - let args = [("ab", false); ("cd", true); ("abcdx", false)] + let args = [("ab", false); ("cd", true); ("bc", false); ("abcdx", false); ("abcd", true)] for arg in args do "abcd".EndsWith(fst arg) |> equal (snd arg) + + testCase "String.EndsWith with OrdinalIgnoreCase works" <| fun () -> + let args = [("ab", false); ("CD", true); ("cd", true); ("bc", false); ("xabcd", false); ("abcd", true)] + for arg in args do + "ABCD".EndsWith(fst arg, StringComparison.OrdinalIgnoreCase) + |> equal (snd arg) testCase "String.Trim works" <| fun () -> " abc ".Trim()