String with triple quote inside of string with triple quote

Viewed 4340

I'm using string with triple quote inside of string with triple quote:

fun main(args: Array<String>) {    
    val firstStr = """

    OLOLO
    """.trimIndent()

    val secondStr = """
    $firstStr
    new added text
    """.trimIndent()

    println("The firstStr is $firstStr")
    println("The secondStr is $secondStr")
}

and I've got the following output:

The firstStr is 
OLOLO
The secondStr is     
OLOLO
    new added text

And I'm confused why the indent remains on the last line (" new added text")... Because I expected that trimIndent() method had to trim all indent (as documentation comment says this method

Detects a common minimal indent of all the input lines, removes it from every line and also removes the first and the last lines if they are blank (notice difference blank vs empty).

And at the same time I can cut this indent with trimMargin() with default parameter ("|"):

fun main(args: Array<String>) {    
    val firstStr = """

    OLOLO
    """.trimIndent()

    val secondStr = """
    $firstStr
    |new added text
    """.trimMargin()

    println("The firstStr is $firstStr")
    println("The secondStr is $secondStr")
}

In this case I've got the following output:

The firstStr is 
OLOLO
The secondStr is     
OLOLO
new added text

But I can't understand why trimIndent() doesn't trim that indent...

2 Answers

The issue isn't actually in trimIndent. It's related to how raw string literals work when they contain parameters. You start off with this:

{
    val firstStr = """

    OLOLO
    """.trimIndent()
}

The result of that is that firstStr has this value:

"
OLOLO"

This is then injected into secondStr with this code:

{
    val secondStr = """
    $firstStr
    new added text
    """.trimIndent()
}

Now imagine that you actually typed this in instead of using a parameter value. You'd do it like this (and I'm going to replace a few spaces with underlines to clarify something):

{
    val secondStr = """

____OLOLO
    new added text
    """.trimIndent()
}

That would all work fine. But when the value is injected into the raw string literal, that isn't actually what happens, as it's got no idea that you wanted to put spaces where I put underlines above. As far as the system is concerned you're telling it simply to put in a carriage return then OLOLO. So actually what happens is the value that's generated (before trimIndent is called) is this:

{
    val secondStr = """

OLOLO
    new added text
    """.trimIndent()
}

And then, by the time trimIndent starts working on it, first thing it does is try to look for a common indent, but it can't find one, as one of the lines (OLOLO) isn't indented. So there's nothing it can do. That's why the white space before new added text remains).

You can see this if you put a breakpoint in the implementation of trimIndent to see what the receiver is (i.e. what value of secondStr is before trimIndent starts its work).

So your solution is right. Add a pipe character and use trimMargin. The pipe means there aren't 4 leading spaces before the added text which is why it trims it all correctly.

The weirdness with trimIdent() appears to be caused by having an empty line to start with. It's also slightly picky with how consistent the leading tabs and spaces are throughout, although the leading empty line seems to throw everything out of whack no doubt. In light of that you could include the newline (\n) within your println as a work-around.

fun main(args: Array<String>) {    

    val firstStr =
        """
            OLOLO
        """.trimIndent()

    val secondStr = 
        """
            $firstStr
            new added text
        """.trimIndent()

    println("The firstStr is \n$firstStr")
    println("The secondStr is \n$secondStr")

}

Output:

The firstStr is
OLOLO
The secondStr is
OLOLO
new added text

String.trimIndent(): String Detects a common minimal indent of all the input lines, removes it from every line and also removes the first and the last lines if they are blank (notice difference blank vs empty). Note that blank lines do not affect the detected indent level. In case if there are non-blank lines with no leading whitespace characters (no indent at all) then the common indent is 0, and therefore this function doesn't change the indentation.

Doesn't preserve the original line endings.

The following statement: "notice difference blank vs empty", might hold the key to the riddle, although it's not exactly clear what they mean by that; definitely strange behavior (maybe a bug?).

Related