Provide String.commonPrefixWith and String.commonSuffixWith methods.
Char.isHighSurrogate and Char.isLowSurrogate now available in js-stdlib too.
This commit is contained in:
@@ -39,3 +39,38 @@ public fun Char.equals(other: Char, ignoreCase: Boolean = false): Boolean {
|
||||
if (this.toLowerCase() === other.toLowerCase()) return true
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if this character is a Unicode high-surrogate code unit (also known as leading-surrogate code unit).
|
||||
*/
|
||||
public fun Char.isHighSurrogate(): Boolean = this in MIN_HIGH_SURROGATE..MAX_HIGH_SURROGATE
|
||||
|
||||
/**
|
||||
* Returns `true` if this character is a Unicode low-surrogate code unit (also known as trailing-surrogate code unit).
|
||||
*/
|
||||
public fun Char.isLowSurrogate(): Boolean = this in MIN_LOW_SURROGATE..MAX_LOW_SURROGATE
|
||||
|
||||
// TODO: make these constants public in Char.Companion after resolving #KT-7271
|
||||
/**
|
||||
* The minimum value of a Unicode high-surrogate code unit
|
||||
*/
|
||||
private val MIN_HIGH_SURROGATE: Char
|
||||
get() = '\uD800'
|
||||
|
||||
/**
|
||||
* The maximum value of a Unicode high-surrogate code unit
|
||||
*/
|
||||
private val MAX_HIGH_SURROGATE: Char
|
||||
get() = '\uDBFF'
|
||||
|
||||
/**
|
||||
* The minimum value of a Unicode low-surrogate code unit
|
||||
*/
|
||||
private val MIN_LOW_SURROGATE: Char
|
||||
get() = '\uDC00'
|
||||
|
||||
/**
|
||||
* The maximum value of a Unicode low-surrogate code unit
|
||||
*/
|
||||
private val MAX_LOW_SURROGATE: Char
|
||||
get() = '\uDFFF'
|
||||
@@ -26,10 +26,6 @@ public fun Char.isDefined(): Boolean = Character.isDefined(this)
|
||||
*/
|
||||
public fun Char.isDigit(): Boolean = Character.isDigit(this)
|
||||
|
||||
/**
|
||||
* Returns `true` if this character is a Unicode high-surrogate code unit (also known as leading-surrogate code unit).
|
||||
*/
|
||||
public fun Char.isHighSurrogate(): Boolean = Character.isHighSurrogate(this)
|
||||
|
||||
/**
|
||||
* Returns `true` if this character (Unicode code point) should be regarded as an ignorable
|
||||
|
||||
@@ -187,6 +187,12 @@ public fun String?.orEmpty(): String = this ?: ""
|
||||
public val String.indices: IntRange
|
||||
get() = 0..length() - 1
|
||||
|
||||
/**
|
||||
* Returns the index of the last character in the String or -1 if the String is empty
|
||||
*/
|
||||
public val String.lastIndex: Int
|
||||
get() = this.length() - 1
|
||||
|
||||
/**
|
||||
* Returns a character at the given index in a [CharSequence]. Allows to use the
|
||||
* index operator for working with character sequences:
|
||||
@@ -197,10 +203,13 @@ public val String.indices: IntRange
|
||||
public fun CharSequence.get(index: Int): Char = this.charAt(index)
|
||||
|
||||
/**
|
||||
* Returns the index of the last character in the String or -1 if the String is empty
|
||||
* Returns `true` if this CharSequence has Unicode surrogate pair at the specified [index]
|
||||
*/
|
||||
public val String.lastIndex: Int
|
||||
get() = this.length() - 1
|
||||
public fun CharSequence.hasSurrogatePairAt(index: Int): Boolean {
|
||||
return index in 0..length() - 2
|
||||
&& this[index].isHighSurrogate()
|
||||
&& this[index + 1].isLowSurrogate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subsequence obtained by taking the characters at the given [indices] in this sequence.
|
||||
@@ -508,6 +517,52 @@ public fun String.endsWith(char: Char, ignoreCase: Boolean): Boolean =
|
||||
// TODO: temporary overload to keep binary compatibility, remove after fixing markdown parser
|
||||
public fun String.endsWith(char: Char): Boolean = endsWith(char, ignoreCase = false)
|
||||
|
||||
|
||||
// common prefix and suffix
|
||||
|
||||
/**
|
||||
* Returns the longest string `prefix` such that this string and [other] string both start with this prefix,
|
||||
* taking care not to split surrogate pairs.
|
||||
* If this and [other] have no common prefix, returns the empty string.
|
||||
|
||||
* @param ignoreCase `true` to ignore character case when matching a character. By default `false`.
|
||||
*/
|
||||
public fun CharSequence.commonPrefixWith(other: CharSequence, ignoreCase: Boolean = false): String {
|
||||
val shortestLength = Math.min(this.length(), other.length())
|
||||
|
||||
var i = 0
|
||||
while (i < shortestLength && this[i].equals(other[i], ignoreCase = ignoreCase)) {
|
||||
i++
|
||||
}
|
||||
if (this.hasSurrogatePairAt(i - 1) || other.hasSurrogatePairAt(i - 1)) {
|
||||
i--
|
||||
}
|
||||
return subSequence(0, i).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the longest string `suffix` such that this string and [other] string both end with this suffix,
|
||||
* taking care not to split surrogate pairs.
|
||||
* If this and [other] have no common suffix, returns the empty string.
|
||||
|
||||
* @param ignoreCase `true` to ignore character case when matching a character. By default `false`.
|
||||
*/
|
||||
public fun CharSequence.commonSuffixWith(other: CharSequence, ignoreCase: Boolean = false): String {
|
||||
val thisLength = this.length()
|
||||
val otherLength = other.length()
|
||||
val shortestLength = Math.min(thisLength, otherLength)
|
||||
|
||||
var i = 0
|
||||
while (i < shortestLength && this[thisLength - i - 1].equals(other[otherLength - i - 1], ignoreCase = ignoreCase)) {
|
||||
i++
|
||||
}
|
||||
if (this.hasSurrogatePairAt(thisLength - i - 1) || other.hasSurrogatePairAt(otherLength - i - 1)) {
|
||||
i--;
|
||||
}
|
||||
return subSequence(thisLength - i, thisLength).toString();
|
||||
}
|
||||
|
||||
|
||||
// indexOfAny()
|
||||
|
||||
private fun String.findAnyOf(chars: CharArray, startIndex: Int, ignoreCase: Boolean, last: Boolean): Pair<Int, Char>? {
|
||||
|
||||
@@ -40,11 +40,12 @@ public fun String.equalsIgnoreCase(anotherString: String): Boolean = equals(anot
|
||||
*
|
||||
* @param ignoreCase `true` to ignore character case when comparing strings. By default `false`.
|
||||
*/
|
||||
public fun String.equals(anotherString: String, ignoreCase: Boolean = false): Boolean =
|
||||
if (!ignoreCase)
|
||||
public fun String.equals(anotherString: String, ignoreCase: Boolean = false): Boolean {
|
||||
return if (!ignoreCase)
|
||||
(this as java.lang.String).equals(anotherString)
|
||||
else
|
||||
(this as java.lang.String).equalsIgnoreCase(anotherString)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this string with all occurrences of [oldChar] replaced with [newChar].
|
||||
|
||||
@@ -70,6 +70,39 @@ class StringTest {
|
||||
assertFalse("".endsWith('a'))
|
||||
}
|
||||
|
||||
test fun commonPrefix() {
|
||||
assertEquals("", "".commonPrefixWith(""))
|
||||
assertEquals("", "any".commonPrefixWith(""))
|
||||
assertEquals("", "".commonPrefixWith("any"))
|
||||
assertEquals("", "some".commonPrefixWith("any"))
|
||||
|
||||
assertEquals("an", "annual".commonPrefixWith("any"))
|
||||
assertEquals("an", "annual".commonPrefixWith("Any", ignoreCase = true))
|
||||
assertEquals("", "annual".commonPrefixWith("Any", ignoreCase = false))
|
||||
// surrogate pairs
|
||||
val dth54 = "\uD83C\uDC58" // domino tile horizontal 5-4
|
||||
val dth55 = "\uD83C\uDC59" // domino tile horizontal 5-5
|
||||
assertEquals("", dth54.commonPrefixWith(dth55))
|
||||
assertEquals(dth54, "$dth54$dth54".commonPrefixWith("$dth54$dth55"))
|
||||
|
||||
}
|
||||
|
||||
test fun commonSuffix() {
|
||||
assertEquals("", "".commonSuffixWith(""))
|
||||
assertEquals("", "any".commonSuffixWith(""))
|
||||
assertEquals("", "".commonSuffixWith("any"))
|
||||
assertEquals("", "some".commonSuffixWith("any"))
|
||||
|
||||
assertEquals("ly", "yearly".commonSuffixWith("monthly"))
|
||||
assertEquals("strö", "strö".commonSuffixWith("BISTRÖ", ignoreCase = true))
|
||||
assertEquals("", "yearly".commonSuffixWith("HARDLY", ignoreCase = false))
|
||||
// surrogate pairs
|
||||
val dth54 = "\uD83C\uDC58" // domino tile horizontal 5-4
|
||||
val kimono = "\uD83D\uDC58" // kimono
|
||||
assertEquals("", dth54.commonSuffixWith(kimono))
|
||||
assertEquals("$dth54", "d$dth54".commonSuffixWith("s$dth54"))
|
||||
}
|
||||
|
||||
test fun capitalize() {
|
||||
assertEquals("A", "A".capitalize())
|
||||
assertEquals("A", "a".capitalize())
|
||||
|
||||
Reference in New Issue
Block a user