Multiline string literal indent handling is unconvient

I want to print:

line line

I must write:

class TestMultiLine {
    Test fun test() {
        println("""line
line""")
    }
}

This way breaks code formatting.

Please, consider Fantom approach:

class TestMultiLine {
    Test fun test() {
        println("""line
                   line""")
    }
}
1 Like

Maybe something like this would be good enough for you?  It's inspired by the same method `stripMargin` in Scala:

fun String.stripMargin(): String {
  val lines = this.split(“n”)
  return lines map { it.replaceAll("^\s*|", “”) } join “n”
}


fun main(args: Array<String>) {
  println(""“Apples, oranges,
  |bananas, kiwis,
  |  avocados, tomatoes”"".stripMargin())


}


You could shorten the name, or make it a val so no need for parentheses.

Rob

I’m not a fan of Fantom’s approach, but I would like to see something like Scala`s stripMargin in the standard library.

There are two convenient functions in stdlib for stripping margin from multiline strings: trimMargin and trimIndent.

	fun test() {
        println("""|line
                   |line""".trimMargin())
        
        println("""
            line
            line
            """.trimIndent())        
    }
4 Likes

The problem with that approach is the trimming is done at run time. It would be nice to have a way to tell the compiler to do it at compile time.

3 Likes

This seems like an ideal candidate for a compiler (not JVM) intrinsic. The nice thing about standard libraries is that functions from it can be replaced by intrinsics if so desired. Their API’s are kind of frozen by default anyway.

1 Like

I completely agree. A lot of time I write SQLs, HTML and other similar things in my code. Would be very nice to have such a functionality. And multiline string keeping extra left spaces is just crazy. This create a very strange code or a very strange string. I don’t know what would be the best rule the follow when removing this extra spaces, but I am sure it is possible to figure out some solution.

Leonardo Priori

1 Like

Xtend has solved this really nicely:
https://eclipse.org/xtend/documentation/203_xtend_expressions.html#templates

Perhaps some inspiration there?

1 Like

The “solution” is in the String.trim... functions that you can use to trim as desired. If these functions could now be defined as constexpr and evaluated at compile time your problem would go away. Alternatively, it may be possible to have a compiler plugin that recognizes these specific function calls and in case of a compile time constant parameter replaces it with the result of calling the function - constexpr is a lot cleaner as it is not limited to “special cases”.

2 Likes

Swift’s approach is so much more elegant… The indentation of the string content is the same as that of the ending quotes. Always and without exception, checked and optimized at compile time. What more could you want?

1 Like

Kotlin 1.3.40 will actually support intrinsics for these functions, see https://youtrack.jetbrains.com/issue/KT-17755.

2 Likes

Intriniscs only work for constant strings, which is not that useful.

As soon as you write something like

x = """|Hello, $name!
       |How are you?""".trimMargin()

the trimming can’t be done at compile time, because Kotlin doesn’t know if name contains line breaks.

Compile-time trimming is still useful for dynamic strings in my opinion. If the dynamic parts do not contain line breaks, it will work very well.

I agree that there should be an option to do the trimming at runtime. In many use cases, the developer will know whether to expect line breaks in the dynamic parts, and can choose accordingly.

It’s useful, yes, but it can’t be done if it doesn’t work properly. Compile-time trimming would require a different syntax.

Yes I see how the current implementation can break existing code.

Correct me if I’m wrong but I don’t think tirmMargin or trimIndent are ever used with dynamic multiline strings. I don’t have real numbers for this but I’d imagine that in most cases a multiline string in a dynamic trimMargin is just a bug and not intended, so maybe changing this is not as bad of a breaking change than it seems.

It may also be possible to translate the dynamic bits of the string into a call that is wrapped in its own trim function that can is given the needed information for correct wrapping by the compiler. It would probably be best implemented in an optimistic fashion (assuming that it doesn’t need to wrap).