Skip to content

Commit a0001fc

Browse files
committed
Adds a margin stripping string interpolator.
Currently only for compiler internal use. Designed to avoid surprises if the interpolated values themselves contain the margin delimiter. Before: val bip = "\n |.." s"""fooo |bar $bip |baz""".stripMargin "fooo bar .. baz" After: sm"""fooo |bar $bip |baz""" "fooo bar |.. baz"
1 parent 6cb08a1 commit a0001fc

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

src/reflect/scala/reflect/internal/SymbolTable.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,12 @@ abstract class SymbolTable extends macros.Universe
345345
/** Is this symbol table a part of a compiler universe?
346346
*/
347347
def isCompilerUniverse = false
348+
349+
/**
350+
* Adds the `sm` String interpolator to a [[scala.StringContext]].
351+
*/
352+
implicit val StringContextStripMarginOps: StringContext => StringContextStripMarginOps = util.StringContextStripMarginOps
353+
348354
}
349355

350356
object SymbolTableStats {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package scala.reflect
2+
package internal
3+
package util
4+
5+
trait StripMarginInterpolator {
6+
def stringContext: StringContext
7+
8+
/**
9+
* A safe combination of `[[scala.collection.immutable.StringLike#stripMargin]]
10+
* and [[scala.StringContext#raw]].
11+
*
12+
* The margin of each line is defined by whitespace leading up to a '|' character.
13+
* This margin is stripped '''before''' the arguments are interpolated into to string.
14+
*
15+
* String escape sequences are '''not''' processed; this interpolater is designed to
16+
* be used with triple quoted Strings.
17+
*
18+
* {{{
19+
* scala> val foo = "f|o|o"
20+
* foo: String = f|o|o
21+
* scala> sm"""|${foo}
22+
* |"""
23+
* res0: String =
24+
* "f|o|o
25+
* "
26+
* }}}
27+
*/
28+
final def sm(args: Any*): String = {
29+
def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak
30+
def stripTrailingPart(s: String) = {
31+
val (pre, post) = s.span(c => !isLineBreak(c))
32+
pre + post.stripMargin
33+
}
34+
val stripped: List[String] = stringContext.parts.toList match {
35+
case head :: tail => head.stripMargin :: (tail map stripTrailingPart)
36+
case Nil => Nil
37+
}
38+
new StringContext(stripped: _*).raw(args: _*)
39+
}
40+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package scala.reflect
2+
package internal
3+
4+
package object util {
5+
/**
6+
* Adds the `sm` String interpolator to a [[scala.StringContext]].
7+
*/
8+
implicit class StringContextStripMarginOps(val stringContext: StringContext) extends StripMarginInterpolator
9+
}

test/files/run/sm-interpolator.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
object Test extends App {
2+
import scala.reflect.internal.util.StringContextStripMarginOps
3+
def check(actual: Any, expected: Any) = if (actual != expected) sys.error(s"expected: [$expected], actual: [$actual])")
4+
5+
val bar = "|\n ||"
6+
7+
check(
8+
sm"""|ab
9+
|de
10+
|${bar} | ${1}""",
11+
"ab \nde\n|\n || | 1")
12+
13+
check(
14+
sm"|",
15+
"")
16+
17+
check(
18+
sm"${0}",
19+
"0")
20+
21+
check(
22+
sm"${0}",
23+
"0")
24+
25+
check(
26+
sm"""${0}|${1}
27+
|""",
28+
"0|1\n")
29+
30+
check(
31+
sm""" ||""",
32+
"|")
33+
34+
check(
35+
sm""" ${" "} ||""",
36+
" ||")
37+
38+
check(
39+
sm"\n",
40+
raw"\n".stripMargin)
41+
}

0 commit comments

Comments
 (0)