Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indentation preserving string interpolation in scala

Tags:

scala

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
like image 355
Sriram Srinivasan Avatar asked Dec 12 '22 00:12

Sriram Srinivasan


1 Answers

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.

Usage:

import Indenter._ and use string interpolation like so e" $foo "

Example
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

Here's the custom indenting context.

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)
}
like image 196
Sriram Srinivasan Avatar answered Jan 10 '23 03:01

Sriram Srinivasan