I was wondering if there is any way of preserving indentation while doing string interpolation in scala. Essentially, I was wondering if I could interpose my own StringContext. Macros would address this problem, but I'd like to wait until they are official.
This is what I want:
val x = "line1 \nline2"
val str = s"> ${x}"
str should evaluate to
> line1
line2
Answering my question, and converting Daniel Sobral's very helpful answer to code. Hopefully it will be of use to someone else with the same issue. I have not used implicit classes since I am still pre-2.10.
import Indenter._
and use string interpolation like so e" $foo "
import Indenter._
object Ex extends App {
override def main(args: Array[String]) {
val name = "Foo"
val fields = "x: Int\ny:String\nz:Double"
// fields has several lines. All of them will be indented by the same amount.
print (e"""
class $name {
${fields}
}
""")
}
}
should print
class Foo
x: Int
y: String
z: Double
class IndentStringContext(sc: StringContext) {
def e(args: Any*):String = {
val sb = new StringBuilder()
for ((s, a) <- sc.parts zip args) {
sb append s
val ind = getindent(s)
if (ind.size > 0) {
sb append a.toString().replaceAll("\n", "\n" + ind)
} else {
sb append a.toString()
}
}
if (sc.parts.size > args.size)
sb append sc.parts.last
sb.toString()
}
// get white indent after the last new line, if any
def getindent(str: String): String = {
val lastnl = str.lastIndexOf("\n")
if (lastnl == -1) ""
else {
val ind = str.substring(lastnl + 1)
if (ind.trim.isEmpty) ind // ind is all whitespace. Use this
else ""
}
}
}
object Indenter {
// top level implicit defs allowed only in 2.10 and above
implicit def toISC(sc: StringContext) = new IndentStringContext(sc)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With