src/Pure/General/pretty.scala
author wenzelm
Sun, 09 May 2010 13:12:22 +0200
changeset 36763 096ebe74aeaf
parent 36745 403585a89772
child 36817 ed97e877ff2d
permissions -rw-r--r--
static Symbol.spaces;
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 =
36763
096ebe74aeaf static Symbol.spaces;
wenzelm
parents: 36745
diff changeset
    33
      XML.Elem(Markup.BREAK, List((Markup.WIDTH, width.toString)),
096ebe74aeaf static Symbol.spaces;
wenzelm
parents: 36745
diff changeset
    34
        List(XML.Text(Symbol.spaces(width))))
36683
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    35
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    36
    def unapply(tree: XML.Tree): Option[Int] =
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    37
      tree match {
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    38
        case XML.Elem(Markup.BREAK, List((Markup.WIDTH, width)), _) => Markup.parse_int(width)
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    39
        case _ => None
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    40
      }
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    41
  }
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
    42
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
    43
  val FBreak = XML.Text("\n")
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    44
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    45
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    46
  /* formatted output */
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    47
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
    48
  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
    49
  {
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
    50
    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
    51
    def string(s: String): Text = copy(tx = XML.Text(s) :: tx, pos = pos + s.length)
36763
096ebe74aeaf static Symbol.spaces;
wenzelm
parents: 36745
diff changeset
    52
    def blanks(wd: Int): Text = string(Symbol.spaces(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
    53
    def content: List[XML.Tree] = tx.reverse
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    54
  }
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    55
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    56
  private def breakdist(trees: List[XML.Tree], after: Int): Int =
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    57
    trees match {
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    58
      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
    59
      case FBreak :: _ => 0
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    60
      case XML.Elem(_, _, body) :: ts =>
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    61
        (0 /: body)(_ + XML.content_length(_)) + breakdist(ts, after)
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    62
      case XML.Text(s) :: ts => s.length + breakdist(ts, after)
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    63
      case Nil => after
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    64
    }
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    65
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    66
  private def forcenext(trees: List[XML.Tree]): List[XML.Tree] =
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    67
    trees match {
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    68
      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
    69
      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
    70
      case Break(_) :: ts => FBreak :: ts
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    71
      case t :: ts => t :: forcenext(ts)
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    72
    }
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    73
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
    74
  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
    75
    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
    76
      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
    77
      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
    78
        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
    79
          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
    80
    }
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
    81
36745
403585a89772 unified/simplified Pretty.margin_default;
wenzelm
parents: 36736
diff changeset
    82
  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
    83
36745
403585a89772 unified/simplified Pretty.margin_default;
wenzelm
parents: 36736
diff changeset
    84
  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
    85
  {
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    86
    val breakgain = margin / 20
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    87
    val emergencypos = margin / 2
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    88
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    89
    def format(trees: List[XML.Tree], blockin: Int, after: Int, text: Text): Text =
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    90
      trees match {
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    91
        case Nil => text
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    92
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    93
        case Block(indent, body) :: ts =>
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    94
          val pos1 = text.pos + indent
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    95
          val pos2 = pos1 % emergencypos
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    96
          val blockin1 =
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    97
            if (pos1 < emergencypos) pos1
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    98
            else pos2
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
    99
          val btext = format(body, blockin1, breakdist(ts, after), text)
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   100
          val ts1 = if (text.nl < btext.nl) forcenext(ts) else ts
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   101
          format(ts1, blockin, after, btext)
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   102
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   103
        case Break(wd) :: ts =>
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   104
          if (text.pos + wd <= (margin - breakdist(ts, after)).max(blockin + breakgain))
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   105
            format(ts, blockin, after, text.blanks(wd))
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   106
          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
   107
        case FBreak :: ts => format(ts, blockin, after, text.newline.blanks(blockin))
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   108
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
   109
        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
   110
          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
   111
          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
   112
          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
   113
          format(ts1, blockin, after, btext1)
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   114
        case XML.Text(s) :: ts => format(ts, blockin, after, text.string(s))
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   115
      }
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
   116
    format(input.flatMap(standard_format), 0, 0, Text()).content
36687
58020b59baf7 basic formatting of pretty trees;
wenzelm
parents: 36683
diff changeset
   117
  }
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
   118
36745
403585a89772 unified/simplified Pretty.margin_default;
wenzelm
parents: 36736
diff changeset
   119
  def string_of(input: List[XML.Tree], margin: Int = margin_default): String =
36736
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   120
    formatted(input).iterator.flatMap(XML.content).mkString
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   121
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   122
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   123
  /* unformatted output */
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   124
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   125
  def unformatted(input: List[XML.Tree]): List[XML.Tree] =
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   126
  {
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   127
    def fmt(tree: XML.Tree): List[XML.Tree] =
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   128
      tree match {
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   129
        case Block(_, body) => body.flatMap(fmt)
36763
096ebe74aeaf static Symbol.spaces;
wenzelm
parents: 36745
diff changeset
   130
        case Break(wd) => List(XML.Text(Symbol.spaces(wd)))
36736
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   131
        case FBreak => List(XML.Text(" "))
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   132
        case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(fmt)))
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   133
        case XML.Text(_) => List(tree)
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   134
      }
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   135
    input.flatMap(standard_format).flatMap(fmt)
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   136
  }
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   137
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   138
  def str_of(input: List[XML.Tree]): String =
93753a8c9550 unformatted output;
wenzelm
parents: 36734
diff changeset
   139
    unformatted(input).iterator.flatMap(XML.content).mkString
36683
41a1210519fd basic support for symbolic pretty printing;
wenzelm
parents:
diff changeset
   140
}