diff options
author | Martin Odersky <odersky@gmail.com> | 2013-05-14 12:22:35 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-05-14 12:22:35 +0200 |
commit | fec318dedaa10bf7ffaebd7aaf4a99e05ac0312a (patch) | |
tree | 8d5f9560806325c922eceb847f5c13b096aa7013 /src/dotty/tools/dotc/printing/Texts.scala | |
parent | b866c49bde62ee8ee52358dee746db64741b5891 (diff) | |
download | dotty-fec318dedaa10bf7ffaebd7aaf4a99e05ac0312a.tar.gz dotty-fec318dedaa10bf7ffaebd7aaf4a99e05ac0312a.tar.bz2 dotty-fec318dedaa10bf7ffaebd7aaf4a99e05ac0312a.zip |
Refactored Printers, Showable and Text into new package dotc.printing.
Diffstat (limited to 'src/dotty/tools/dotc/printing/Texts.scala')
-rw-r--r-- | src/dotty/tools/dotc/printing/Texts.scala | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/printing/Texts.scala b/src/dotty/tools/dotc/printing/Texts.scala new file mode 100644 index 000000000..2ff4edc9a --- /dev/null +++ b/src/dotty/tools/dotc/printing/Texts.scala @@ -0,0 +1,153 @@ +package dotty.tools.dotc +package printing + +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) + } + + object Text { + def apply(): Text = Str("") + def apply(xs: Traversable[Text], sep: String = " "): Text = { + val ys = xs filterNot (_.isEmpty) + if (ys.isEmpty) Str("") + else ys reduce (_ ~ sep ~ _) + } + 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) +}
\ No newline at end of file |