src/Pure/General/pretty.scala
author wenzelm
Sat, 08 May 2010 19:14:13 +0200
changeset 36754 403585a89772
parent 36736 93753a8c9550
child 36772 096ebe74aeaf
permissions -rw-r--r--
unified/simplified Pretty.margin_default;
discontinued special Pretty.setmargin etc;
explicit margin argument for Pretty.string_of_margin etc.;
wenzelm@36721
     1
/*  Title:      Pure/General/pretty.scala
wenzelm@36721
     2
    Author:     Makarius
wenzelm@36721
     3
wenzelm@36725
     4
Generic pretty printing module.
wenzelm@36721
     5
*/
wenzelm@36721
     6
wenzelm@36721
     7
package isabelle
wenzelm@36721
     8
wenzelm@36721
     9
wenzelm@36721
    10
object Pretty
wenzelm@36721
    11
{
wenzelm@36725
    12
  /* markup trees with physical blocks and breaks */
wenzelm@36725
    13
wenzelm@36721
    14
  object Block
wenzelm@36721
    15
  {
wenzelm@36721
    16
    def apply(indent: Int, body: List[XML.Tree]): XML.Tree =
wenzelm@36721
    17
      XML.Elem(Markup.BLOCK, List((Markup.INDENT, indent.toString)), body)
wenzelm@36721
    18
wenzelm@36721
    19
    def unapply(tree: XML.Tree): Option[(Int, List[XML.Tree])] =
wenzelm@36721
    20
      tree match {
wenzelm@36721
    21
        case XML.Elem(Markup.BLOCK, List((Markup.INDENT, indent)), body) =>
wenzelm@36721
    22
          Markup.parse_int(indent) match {
wenzelm@36721
    23
            case Some(i) => Some((i, body))
wenzelm@36721
    24
            case None => None
wenzelm@36721
    25
          }
wenzelm@36721
    26
        case _ => None
wenzelm@36721
    27
      }
wenzelm@36721
    28
  }
wenzelm@36721
    29
wenzelm@36721
    30
  object Break
wenzelm@36721
    31
  {
wenzelm@36721
    32
    def apply(width: Int): XML.Tree =
wenzelm@36721
    33
      XML.Elem(Markup.BREAK, List((Markup.WIDTH, width.toString)), List(XML.Text(" " * width)))
wenzelm@36721
    34
wenzelm@36721
    35
    def unapply(tree: XML.Tree): Option[Int] =
wenzelm@36721
    36
      tree match {
wenzelm@36721
    37
        case XML.Elem(Markup.BREAK, List((Markup.WIDTH, width)), _) => Markup.parse_int(width)
wenzelm@36721
    38
        case _ => None
wenzelm@36721
    39
      }
wenzelm@36721
    40
  }
wenzelm@36721
    41
wenzelm@36727
    42
  val FBreak = XML.Text("\n")
wenzelm@36725
    43
wenzelm@36725
    44
wenzelm@36725
    45
  /* formatted output */
wenzelm@36725
    46
wenzelm@36734
    47
  private case class Text(tx: List[XML.Tree] = Nil, val pos: Int = 0, val nl: Int = 0)
wenzelm@36725
    48
  {
wenzelm@36734
    49
    def newline: Text = copy(tx = FBreak :: tx, pos = 0, nl = nl + 1)
wenzelm@36734
    50
    def string(s: String): Text = copy(tx = XML.Text(s) :: tx, pos = pos + s.length)
wenzelm@36725
    51
    def blanks(wd: Int): Text = string(" " * wd)
wenzelm@36734
    52
    def content: List[XML.Tree] = tx.reverse
wenzelm@36725
    53
  }
wenzelm@36725
    54
wenzelm@36725
    55
  private def breakdist(trees: List[XML.Tree], after: Int): Int =
wenzelm@36725
    56
    trees match {
wenzelm@36725
    57
      case Break(_) :: _ => 0
wenzelm@36727
    58
      case FBreak :: _ => 0
wenzelm@36725
    59
      case XML.Elem(_, _, body) :: ts =>
wenzelm@36725
    60
        (0 /: body)(_ + XML.content_length(_)) + breakdist(ts, after)
wenzelm@36725
    61
      case XML.Text(s) :: ts => s.length + breakdist(ts, after)
wenzelm@36725
    62
      case Nil => after
wenzelm@36725
    63
    }
wenzelm@36725
    64
wenzelm@36725
    65
  private def forcenext(trees: List[XML.Tree]): List[XML.Tree] =
wenzelm@36725
    66
    trees match {
wenzelm@36725
    67
      case Nil => Nil
wenzelm@36727
    68
      case FBreak :: _ => trees
wenzelm@36727
    69
      case Break(_) :: ts => FBreak :: ts
wenzelm@36725
    70
      case t :: ts => t :: forcenext(ts)
wenzelm@36725
    71
    }
wenzelm@36725
    72
wenzelm@36734
    73
  private def standard_format(tree: XML.Tree): List[XML.Tree] =
wenzelm@36727
    74
    tree match {
wenzelm@36734
    75
      case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(standard_format)))
wenzelm@36727
    76
      case XML.Text(text) =>
wenzelm@36727
    77
        Library.separate(FBreak,
wenzelm@36727
    78
          Library.chunks(text).toList.map((s: CharSequence) => XML.Text(s.toString)))
wenzelm@36727
    79
    }
wenzelm@36727
    80
wenzelm@36754
    81
  private val margin_default = 76
wenzelm@36734
    82
wenzelm@36754
    83
  def formatted(input: List[XML.Tree], margin: Int = margin_default): List[XML.Tree] =
wenzelm@36725
    84
  {
wenzelm@36725
    85
    val breakgain = margin / 20
wenzelm@36725
    86
    val emergencypos = margin / 2
wenzelm@36725
    87
wenzelm@36725
    88
    def format(trees: List[XML.Tree], blockin: Int, after: Int, text: Text): Text =
wenzelm@36725
    89
      trees match {
wenzelm@36725
    90
        case Nil => text
wenzelm@36725
    91
wenzelm@36725
    92
        case Block(indent, body) :: ts =>
wenzelm@36725
    93
          val pos1 = text.pos + indent
wenzelm@36725
    94
          val pos2 = pos1 % emergencypos
wenzelm@36725
    95
          val blockin1 =
wenzelm@36725
    96
            if (pos1 < emergencypos) pos1
wenzelm@36725
    97
            else pos2
wenzelm@36725
    98
          val btext = format(body, blockin1, breakdist(ts, after), text)
wenzelm@36725
    99
          val ts1 = if (text.nl < btext.nl) forcenext(ts) else ts
wenzelm@36725
   100
          format(ts1, blockin, after, btext)
wenzelm@36725
   101
wenzelm@36725
   102
        case Break(wd) :: ts =>
wenzelm@36725
   103
          if (text.pos + wd <= (margin - breakdist(ts, after)).max(blockin + breakgain))
wenzelm@36725
   104
            format(ts, blockin, after, text.blanks(wd))
wenzelm@36725
   105
          else format(ts, blockin, after, text.newline.blanks(blockin))
wenzelm@36727
   106
        case FBreak :: ts => format(ts, blockin, after, text.newline.blanks(blockin))
wenzelm@36725
   107
wenzelm@36734
   108
        case XML.Elem(name, atts, body) :: ts =>
wenzelm@36734
   109
          val btext = format(body, blockin, breakdist(ts, after), text.copy(tx = Nil))
wenzelm@36734
   110
          val ts1 = if (text.nl < btext.nl) forcenext(ts) else ts
wenzelm@36734
   111
          val btext1 = btext.copy(tx = XML.Elem(name, atts, btext.content) :: text.tx)
wenzelm@36734
   112
          format(ts1, blockin, after, btext1)
wenzelm@36725
   113
        case XML.Text(s) :: ts => format(ts, blockin, after, text.string(s))
wenzelm@36725
   114
      }
wenzelm@36734
   115
    format(input.flatMap(standard_format), 0, 0, Text()).content
wenzelm@36725
   116
  }
wenzelm@36734
   117
wenzelm@36754
   118
  def string_of(input: List[XML.Tree], margin: Int = margin_default): String =
wenzelm@36736
   119
    formatted(input).iterator.flatMap(XML.content).mkString
wenzelm@36736
   120
wenzelm@36736
   121
wenzelm@36736
   122
  /* unformatted output */
wenzelm@36736
   123
wenzelm@36736
   124
  def unformatted(input: List[XML.Tree]): List[XML.Tree] =
wenzelm@36736
   125
  {
wenzelm@36736
   126
    def fmt(tree: XML.Tree): List[XML.Tree] =
wenzelm@36736
   127
      tree match {
wenzelm@36736
   128
        case Block(_, body) => body.flatMap(fmt)
wenzelm@36736
   129
        case Break(wd) => List(XML.Text(" " * wd))
wenzelm@36736
   130
        case FBreak => List(XML.Text(" "))
wenzelm@36736
   131
        case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(fmt)))
wenzelm@36736
   132
        case XML.Text(_) => List(tree)
wenzelm@36736
   133
      }
wenzelm@36736
   134
    input.flatMap(standard_format).flatMap(fmt)
wenzelm@36736
   135
  }
wenzelm@36736
   136
wenzelm@36736
   137
  def str_of(input: List[XML.Tree]): String =
wenzelm@36736
   138
    unformatted(input).iterator.flatMap(XML.content).mkString
wenzelm@36721
   139
}