aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/printing/Texts.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/printing/Texts.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/printing/Texts.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/printing/Texts.scala168
1 files changed, 168 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala
new file mode 100644
index 000000000..db81cab7a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala
@@ -0,0 +1,168 @@
+package dotty.tools.dotc
+package printing
+import core.Contexts.Context
+import language.implicitConversions
+
+object Texts {
+
+ abstract class Text {
+
+ protected def indentMargin = 2
+
+ def relems: List[Text]
+
+ def isEmpty: Boolean = this match {
+ case Str(s) => s.isEmpty
+ case Fluid(relems) => relems forall (_.isEmpty)
+ case Vertical(relems) => relems.isEmpty
+ }
+
+ def isVertical = isInstanceOf[Vertical]
+ def isClosed = isVertical || isInstanceOf[Closed]
+ def isFluid = isInstanceOf[Fluid]
+ def isSplittable = isFluid && !isClosed
+
+ def close = new Closed(relems)
+
+ def remaining(width: Int): Int = this match {
+ case Str(s) =>
+ width - s.length
+ case Fluid(Nil) =>
+ width
+ case Fluid(last :: prevs) =>
+ val r = last remaining width
+ if (r < 0) r else Fluid(prevs) remaining r
+ case Vertical(_) =>
+ -1
+ }
+
+ def lastLine: String = this match {
+ case Str(s) => s
+ case _ => relems.head.lastLine
+ }
+
+ def appendToLastLine(that: Text): Text = that match {
+ case Str(s2) =>
+ this match {
+ case Str(s1) => Str(s1 + s2)
+ case Fluid(Str(s1) :: prev) => Fluid(Str(s1 + s2) :: prev)
+ case Fluid(relems) => Fluid(that :: relems)
+ }
+ case Fluid(relems) =>
+ (this /: relems.reverse)(_ appendToLastLine _)
+ }
+
+ private def appendIndented(that: Text)(width: Int): Text =
+ Vertical(that.layout(width - indentMargin).indented :: this.relems)
+
+ private def append(width: Int)(that: Text): Text = {
+ if (this.isEmpty) that.layout(width)
+ else if (that.isEmpty) this
+ else if (that.isVertical) appendIndented(that)(width)
+ else if (this.isVertical) Fluid(that.layout(width) :: this.relems)
+ else if (that.remaining(width - lastLine.length) >= 0) appendToLastLine(that)
+ else if (that.isSplittable) (this /: that.relems.reverse)(_.append(width)(_))
+ else appendIndented(that)(width)
+ }
+
+ def layout(width: Int): Text = this match {
+ case Str(_) =>
+ this
+ case Fluid(relems) =>
+ ((Str(""): Text) /: relems.reverse)(_.append(width)(_))
+ case Vertical(relems) =>
+ Vertical(relems map (_ layout width))
+ }
+
+ def map(f: String => String): Text = this match {
+ case Str(s) => Str(f(s))
+ case Fluid(relems) => Fluid(relems map (_ map f))
+ case Vertical(relems) => Vertical(relems map (_ map f))
+ }
+
+ def stripPrefix(pre: String): Text = this match {
+ case Str(s) =>
+ if (s.startsWith(pre)) s drop pre.length else s
+ case Fluid(relems) =>
+ val elems = relems.reverse
+ val head = elems.head.stripPrefix(pre)
+ if (head eq elems.head) this else Fluid((head :: elems.tail).reverse)
+ case Vertical(relems) =>
+ val elems = relems.reverse
+ val head = elems.head.stripPrefix(pre)
+ if (head eq elems.head) this else Vertical((head :: elems.tail).reverse)
+ }
+
+ private def indented: Text = this match {
+ case Str(s) => Str((" " * indentMargin) + s)
+ case Fluid(relems) => Fluid(relems map (_.indented))
+ case Vertical(relems) => Vertical(relems map (_.indented))
+ }
+
+ def print(sb: StringBuilder): Unit = this match {
+ case Str(s) =>
+ sb.append(s)
+ case _ =>
+ var follow = false
+ for (elem <- relems.reverse) {
+ if (follow) sb.append("\n")
+ elem.print(sb)
+ follow = true
+ }
+ }
+
+ def mkString(width: Int): String = {
+ val sb = new StringBuilder
+ layout(width).print(sb)
+ sb.toString
+ }
+
+ def ~ (that: Text) =
+ if (this.isEmpty) that
+ else if (that.isEmpty) this
+ else Fluid(that :: this :: Nil)
+
+ def ~~ (that: Text) =
+ if (this.isEmpty) that
+ else if (that.isEmpty) this
+ else Fluid(that :: Str(" ") :: this :: Nil)
+
+ def over (that: Text) =
+ if (this.isVertical) Vertical(that :: this.relems)
+ else Vertical(that :: this :: Nil)
+
+ def provided(pred: Boolean) = if (pred) this else Str("")
+ }
+
+ object Text {
+
+ /** The empty text */
+ def apply(): Text = Str("")
+
+ /** A concatenation of elements in `xs` and interspersed with
+ * separator strings `sep`.
+ */
+ def apply(xs: Traversable[Text], sep: String = " "): Text = {
+ if (sep == "\n") lines(xs)
+ else {
+ val ys = xs filterNot (_.isEmpty)
+ if (ys.isEmpty) Str("")
+ else ys reduce (_ ~ sep ~ _)
+ }
+ }
+
+ /** The given texts `xs`, each on a separate line */
+ def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse)
+ }
+
+ case class Str(s: String) extends Text {
+ override def relems: List[Text] = List(this)
+ }
+
+ case class Vertical(relems: List[Text]) extends Text
+ case class Fluid(relems: List[Text]) extends Text
+
+ class Closed(relems: List[Text]) extends Fluid(relems)
+
+ implicit def stringToText(s: String): Text = Str(s)
+}