blob: db81cab7afaaba0de0f191e7af2b025345ebe3df (
plain) (
tree)
|
|
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)
}
|