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