diff --git a/js/js.libraries/src/core/stringsCode.kt b/js/js.libraries/src/core/stringsCode.kt index 9975e9aeec5..91492a89143 100644 --- a/js/js.libraries/src/core/stringsCode.kt +++ b/js/js.libraries/src/core/stringsCode.kt @@ -87,3 +87,13 @@ public fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolea public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String = nativeReplace(RegExp(Regex.escape(oldChar.toString()), if (ignoreCase) "gi" else "g"), newChar.toString()) + +deprecated("Use replaceFirst(String, String) instead.") +public fun String.replaceFirstLiteral(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = + nativeReplace(RegExp(Regex.escape(oldValue), if (ignoreCase) "i" else ""), Regex.escapeReplacement(newValue)) + +public fun String.replaceFirst(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = + nativeReplace(RegExp(Regex.escape(oldValue), if (ignoreCase) "i" else ""), Regex.escapeReplacement(newValue)) + +public fun String.replaceFirst(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String = + nativeReplace(RegExp(Regex.escape(oldChar.toString()), if (ignoreCase) "i" else ""), newChar.toString()) diff --git a/libraries/stdlib/src/kotlin/text/Strings.kt b/libraries/stdlib/src/kotlin/text/Strings.kt index faeafbb81cd..21c6c7d2c87 100644 --- a/libraries/stdlib/src/kotlin/text/Strings.kt +++ b/libraries/stdlib/src/kotlin/text/Strings.kt @@ -515,6 +515,14 @@ public fun String.replace(regex: Regex, replacement: String): String = regex.rep */ public fun String.replace(regex: Regex, transform: (MatchResult) -> String): String = regex.replace(this, transform) +/** + * Replaces the first occurrence of the given regular expression [regex] in this string with specified [replacement] expression. + * + * @param replacement A replacement expression that can include substitutions. See [Regex.replaceFirst] for details. + */ +public fun String.replaceFirst(regex: Regex, replacement: String): String = regex.replaceFirst(this, replacement) + + /** * Returns `true` if this string matches the given regular expression. */ diff --git a/libraries/stdlib/src/kotlin/text/StringsJVM.kt b/libraries/stdlib/src/kotlin/text/StringsJVM.kt index 5d0f3aafb96..e0feec8b208 100644 --- a/libraries/stdlib/src/kotlin/text/StringsJVM.kt +++ b/libraries/stdlib/src/kotlin/text/StringsJVM.kt @@ -49,7 +49,7 @@ public fun String.equals(anotherString: String, ignoreCase: Boolean = false): Bo } /** - * Returns a copy of this string with all occurrences of [oldChar] replaced with [newChar]. + * Returns a new string with all occurrences of [oldChar] replaced with [newChar]. */ public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String { if (!ignoreCase) @@ -65,6 +65,25 @@ public fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = fa public fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = splitToSequence(oldValue, ignoreCase = ignoreCase).joinToString(separator = newValue) + +/** + * Returns a new string with the first occurrence of [oldChar] replaced with [newChar]. + */ +public fun String.replaceFirst(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String { + val index = indexOf(oldChar, ignoreCase = ignoreCase) + return if (index < 0) this else this.replaceRange(index, index + 1, newChar.toString()) +} + +/** + * Returns a new string obtained by replacing the first occurrence of the [oldValue] substring in this string + * with the specified [newValue] string. + * + * Soon shall be renamed back to replaceFirst. + */ +public fun String.replaceFirstLiteral(oldValue: String, newValue: String, ignoreCase: Boolean = false): String { + val index = indexOf(oldValue, ignoreCase = ignoreCase) + return if (index < 0) this else this.replaceRange(index, index + oldValue.length(), newValue) +} /** * Returns a new string obtained by replacing each substring of this string that matches the given regular expression * with the given [replacement]. @@ -231,6 +250,7 @@ public fun String(stringBuilder: java.lang.StringBuilder): String = java.lang.St /** * Replaces the first substring of this string that matches the given regular expression with the given replacement. */ +deprecated("Use replaceFirst(Regex, String) or replaceFirstLiteral(String, String) instead.") public fun String.replaceFirst(regex: String, replacement: String): String = (this as java.lang.String).replaceFirst(regex, replacement) /** diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index a0334e73eb8..9e9c9da078c 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -543,4 +543,19 @@ class StringTest { assertEquals("-a-b-b-A-b-", input.replace("", "-")) } + + test fun replaceFirst() { + val input = "AbbabA" + assertEquals("Abb${'$'}bA", input.replaceFirst('a','$')) + assertEquals("${'$'}bbabA", input.replaceFirst('a','$', ignoreCase = true)) + // doesn't pass in Rhino JS + // assertEquals("schrodinger", "schrÖdinger".replaceFirst('ö', 'o', ignoreCase = true)) + + assertEquals("Abba${'$'}", input.replaceFirstLiteral("bA", "$")) + assertEquals("Ab${'$'}bA", input.replaceFirstLiteral("bA", "$", ignoreCase = true)) + + assertEquals("-test", "test".replaceFirstLiteral("", "-")) + } + + }