#KT-8708 refactor trimIndent, trimMargin, replaceIndent, replaceIndentByMargin, prependIndent
This commit is contained in:
@@ -0,0 +1,115 @@
|
|||||||
|
package kotlin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trims leading whitespace characters followed by [marginPrefix] from every line of a source string and removes
|
||||||
|
* first and last lines if they are blank (notice difference blank vs empty). Doesn't affect line if it doesn't contain
|
||||||
|
* [marginPrefix] except first and last blank lines
|
||||||
|
*
|
||||||
|
* Do not preserves original line endings
|
||||||
|
*
|
||||||
|
* [marginPrefix] should be non-blank string
|
||||||
|
*
|
||||||
|
* Example
|
||||||
|
* ```kotlin
|
||||||
|
* assertEquals("ABC\n123\n456", """ABC
|
||||||
|
* |123
|
||||||
|
* |456""".trimMargin())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param marginPrefix characters to be used as a margin delimiter. Default is `|` (pipe character)
|
||||||
|
* @return the trimmed String
|
||||||
|
* @see kotlin.isWhitespace
|
||||||
|
* @since M13
|
||||||
|
*/
|
||||||
|
public fun String.trimMargin(marginPrefix: String = "|"): String =
|
||||||
|
replaceIndentByMargin("", marginPrefix)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects indent by [marginPrefix] as it does [trimMargin] and replace it with [newIndent]
|
||||||
|
*/
|
||||||
|
public fun String.replaceIndentByMargin(newIndent: String = "", marginPrefix: String = "|"): String {
|
||||||
|
require(marginPrefix.isNotBlank()) { "marginPrefix should be non blank string but it is '$marginPrefix'" }
|
||||||
|
val lines = lines()
|
||||||
|
|
||||||
|
return lines.reindent(length() + newIndent.length() * lines.size(), getIndentFunction(newIndent)) { line ->
|
||||||
|
val firstNonWhitespaceIndex = line.indexOfFirst { !it.isWhitespace() }
|
||||||
|
|
||||||
|
when {
|
||||||
|
firstNonWhitespaceIndex == -1 -> null
|
||||||
|
line.startsWith(marginPrefix, firstNonWhitespaceIndex) -> line.substring(firstNonWhitespaceIndex + marginPrefix.length())
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects a common minimal indent of all the input lines, removes it from every line and also removes first and last
|
||||||
|
* lines if they are blank (notice difference blank vs empty).
|
||||||
|
* Note that blank lines do not affect detected indent level.
|
||||||
|
* Please keep in mind that if there are non-blank lines with no leading whitespace characters (no indent at all) then the
|
||||||
|
* common indent is 0 so this function may do nothing so it is recommended to keep first line empty (will be dropped).
|
||||||
|
*
|
||||||
|
* Do not preserves original line endings
|
||||||
|
*
|
||||||
|
* Example
|
||||||
|
* ```kotlin
|
||||||
|
* assertEquals("ABC\n123\n456", """
|
||||||
|
* ABC
|
||||||
|
* 123
|
||||||
|
* 456""".trimMargin())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @return deindented String
|
||||||
|
* @see kotlin.isBlank
|
||||||
|
* @since M13
|
||||||
|
*/
|
||||||
|
public fun String.trimIndent(): String = replaceIndent("")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects a common minimal indent like it does [trimIndent] and replaces it with the specified [newIndent]
|
||||||
|
* @since M13
|
||||||
|
*/
|
||||||
|
public fun String.replaceIndent(newIndent: String = ""): String {
|
||||||
|
val lines = lines()
|
||||||
|
|
||||||
|
val minCommonIndent = lines
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.map { it.indentWidth() }
|
||||||
|
.min() ?: 0
|
||||||
|
|
||||||
|
return lines.reindent(length() + newIndent.length() * lines.size(), getIndentFunction(newIndent)) { line -> line.drop(minCommonIndent) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepends [indent] to every line of the original string. Does not preserve original line endings
|
||||||
|
*/
|
||||||
|
public fun String.prependIndent(indent: String = " "): String =
|
||||||
|
lineSequence()
|
||||||
|
.map {
|
||||||
|
when {
|
||||||
|
it.isBlank() -> {
|
||||||
|
when {
|
||||||
|
it.length() < indent.length() -> indent
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> indent + it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.joinToString("\n")
|
||||||
|
|
||||||
|
private fun String.indentWidth(): Int = indexOfFirst { !it.isWhitespace() }.let { if (it == -1) length() else it }
|
||||||
|
|
||||||
|
private fun getIndentFunction(indent: String) = when {
|
||||||
|
indent.isEmpty() -> { line: String -> line }
|
||||||
|
else -> { line: String -> "$indent$line" }
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun List<String>.reindent(resultSizeEstimate: Int, indentAddFunction: (String) -> String, indentCutFunction: (String) -> String?) = lastIndex.let { lastLineIndex ->
|
||||||
|
withIndex()
|
||||||
|
.dropWhile { it.index == 0 && it.value.isBlank() }
|
||||||
|
.takeWhile { it.index != lastLineIndex || it.value.isNotBlank() }
|
||||||
|
.map { indentCutFunction(it.value)?.let { cutted -> indentAddFunction(cutted) } ?: it.value }
|
||||||
|
.joinTo(StringBuilder(resultSizeEstimate), "\n")
|
||||||
|
.toString()
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package kotlin
|
package kotlin
|
||||||
|
|
||||||
import java.lang
|
|
||||||
import java.util.NoSuchElementException
|
import java.util.NoSuchElementException
|
||||||
import kotlin.platform.platformName
|
import kotlin.platform.platformName
|
||||||
import kotlin.text.MatchResult
|
import kotlin.text.MatchResult
|
||||||
@@ -792,86 +791,6 @@ public fun String.contains(seq: CharSequence, ignoreCase: Boolean = false): Bool
|
|||||||
public fun String.contains(char: Char, ignoreCase: Boolean = false): Boolean =
|
public fun String.contains(char: Char, ignoreCase: Boolean = false): Boolean =
|
||||||
indexOf(char, ignoreCase = ignoreCase) >= 0
|
indexOf(char, ignoreCase = ignoreCase) >= 0
|
||||||
|
|
||||||
private fun getIndentFunction(indent: String) = when {
|
|
||||||
indent.isEmpty() -> { line: String -> line }
|
|
||||||
else -> { line: String -> "$indent$line" }
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun List<String>.reindent(length: Int, indentAddFunction: (String) -> String, indentCutFunction: (String) -> String) = lastIndex.let { lastLineIndex ->
|
|
||||||
withIndex()
|
|
||||||
.dropWhile { it.index == 0 && it.value.isBlank() }
|
|
||||||
.takeWhile { it.index != lastLineIndex || it.value.isNotBlank() }
|
|
||||||
.map { indentCutFunction(it.value) }
|
|
||||||
.map(indentAddFunction)
|
|
||||||
.joinTo(StringBuilder(length), "\n")
|
|
||||||
.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trims leading whitespace characters followed by [marginPrefix] from every line of a source string and removes
|
|
||||||
* first and last lines if they are blank (notice difference blank vs empty).
|
|
||||||
* Do not preserves original line endings
|
|
||||||
*
|
|
||||||
* Example
|
|
||||||
* ```kotlin
|
|
||||||
* assertEquals("ABC\n123\n456", """ABC
|
|
||||||
* |123
|
|
||||||
* |456""".trimMargin())
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param marginPrefix characters to be used as a margin delimiter. Default is `|` (pipe character)
|
|
||||||
* @return the trimmed String
|
|
||||||
* @see kotlin.isWhitespace
|
|
||||||
* @since M13
|
|
||||||
*/
|
|
||||||
public fun String.trimMargin(marginPrefix: String = "|", newIndent: String = ""): String {
|
|
||||||
require(marginPrefix.isNotBlank()) { "marginPrefix should be non blank string but it is '$marginPrefix'" }
|
|
||||||
|
|
||||||
return lines().reindent(length(), getIndentFunction(newIndent)) { line ->
|
|
||||||
val firstNonWhitespaceIndex = line.indexOfFirst { !it.isWhitespace() }
|
|
||||||
|
|
||||||
when {
|
|
||||||
firstNonWhitespaceIndex == -1 -> line
|
|
||||||
line.startsWith(marginPrefix, firstNonWhitespaceIndex) -> line.substring(firstNonWhitespaceIndex + marginPrefix.length())
|
|
||||||
else -> line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects a common minimal indent of all the input lines, removes it from every line and also removes first and last
|
|
||||||
* lines if they are blank (notice difference blank vs empty).
|
|
||||||
* Note that blank lines do not affect detected indent level.
|
|
||||||
* Please keep in mind that if there are non-blank lines with no leading whitespace characters (no indent at all) then the
|
|
||||||
* common indent is 0 so this function may do nothing so it is recommended to keep first line empty (will be dropped).
|
|
||||||
*
|
|
||||||
* Do not preserves original line endings
|
|
||||||
*
|
|
||||||
* Example
|
|
||||||
* ```kotlin
|
|
||||||
* assertEquals("ABC\n123\n456", """
|
|
||||||
* ABC
|
|
||||||
* 123
|
|
||||||
* 456""".trimMargin())
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @return deindented String
|
|
||||||
* @see kotlin.isBlank
|
|
||||||
* @since M13
|
|
||||||
*/
|
|
||||||
public fun String.trimIndent(newIndent: String = ""): String {
|
|
||||||
val lines = lines()
|
|
||||||
|
|
||||||
val minCommonIndent = lines
|
|
||||||
.filter { it.isNotBlank() }
|
|
||||||
.map { it.indentWidth() }
|
|
||||||
.min() ?: 0
|
|
||||||
|
|
||||||
return lines.reindent(length(), getIndentFunction(newIndent)) { line -> line.drop(minCommonIndent) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.indentWidth(): Int = indexOfFirst { !it.isWhitespace() }.let { if (it == -1) length() else it }
|
|
||||||
|
|
||||||
// rangesDelimitedBy
|
// rangesDelimitedBy
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -566,9 +566,9 @@ class StringTest {
|
|||||||
|123
|
|123
|
||||||
|456""".trimMargin())
|
|456""".trimMargin())
|
||||||
|
|
||||||
assertEquals(" ABC\n 123\n 456", """ABC
|
assertEquals("ABC\n 123\n 456", """ABC
|
||||||
|123
|
|123
|
||||||
|456""".trimMargin(newIndent = " "))
|
|456""".replaceIndentByMargin(newIndent = " "))
|
||||||
|
|
||||||
assertEquals("ABC \n123\n456", """ABC${" "}
|
assertEquals("ABC \n123\n456", """ABC${" "}
|
||||||
|123
|
|123
|
||||||
@@ -626,7 +626,7 @@ class StringTest {
|
|||||||
assertEquals(" 123\n 456", """
|
assertEquals(" 123\n 456", """
|
||||||
123
|
123
|
||||||
456
|
456
|
||||||
""".trimIndent(newIndent = " "))
|
""".replaceIndent(newIndent = " "))
|
||||||
|
|
||||||
assertEquals(" 123\n456", """
|
assertEquals(" 123\n456", """
|
||||||
123
|
123
|
||||||
@@ -668,4 +668,12 @@ ${" "}
|
|||||||
assertEquals(42, indents.max())
|
assertEquals(42, indents.max())
|
||||||
assertEquals(1, deindented.lines().count { it.isEmpty() })
|
assertEquals(1, deindented.lines().count { it.isEmpty() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test fun testIndent() {
|
||||||
|
assertEquals(" ABC\n 123", "ABC\n123".prependIndent(" "))
|
||||||
|
assertEquals(" ABC\n \n 123", "ABC\n\n123".prependIndent(" "))
|
||||||
|
assertEquals(" ABC\n \n 123", "ABC\n \n123".prependIndent(" "))
|
||||||
|
assertEquals(" ABC\n \n 123", "ABC\n \n123".prependIndent(" "))
|
||||||
|
assertEquals(" ", "".prependIndent(" "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user