43 val FBreak = XML.Text("\n") |
43 val FBreak = XML.Text("\n") |
44 |
44 |
45 |
45 |
46 /* formatted output */ |
46 /* formatted output */ |
47 |
47 |
48 private case class Text(tx: List[XML.Tree] = Nil, val pos: Int = 0, val nl: Int = 0) |
|
49 { |
|
50 def newline: Text = copy(tx = FBreak :: tx, pos = 0, nl = nl + 1) |
|
51 def string(s: String): Text = copy(tx = XML.Text(s) :: tx, pos = pos + s.length) |
|
52 def blanks(wd: Int): Text = string(Symbol.spaces(wd)) |
|
53 def content: List[XML.Tree] = tx.reverse |
|
54 } |
|
55 |
|
56 private def breakdist(trees: List[XML.Tree], after: Int): Int = |
|
57 trees match { |
|
58 case Break(_) :: _ => 0 |
|
59 case FBreak :: _ => 0 |
|
60 case XML.Elem(_, _, body) :: ts => |
|
61 (0 /: body)(_ + XML.content_length(_)) + breakdist(ts, after) |
|
62 case XML.Text(s) :: ts => s.length + breakdist(ts, after) |
|
63 case Nil => after |
|
64 } |
|
65 |
|
66 private def forcenext(trees: List[XML.Tree]): List[XML.Tree] = |
|
67 trees match { |
|
68 case Nil => Nil |
|
69 case FBreak :: _ => trees |
|
70 case Break(_) :: ts => FBreak :: ts |
|
71 case t :: ts => t :: forcenext(ts) |
|
72 } |
|
73 |
|
74 private def standard_format(tree: XML.Tree): List[XML.Tree] = |
48 private def standard_format(tree: XML.Tree): List[XML.Tree] = |
75 tree match { |
49 tree match { |
76 case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(standard_format))) |
50 case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(standard_format))) |
77 case XML.Text(text) => |
51 case XML.Text(text) => |
78 Library.separate(FBreak, |
52 Library.separate(FBreak, |
79 Library.chunks(text).toList.map((s: CharSequence) => XML.Text(s.toString))) |
53 Library.chunks(text).toList.map((s: CharSequence) => XML.Text(s.toString))) |
80 } |
54 } |
81 |
55 |
|
56 case class Text(tx: List[XML.Tree] = Nil, val pos: Double = 0.0, val nl: Int = 0) |
|
57 { |
|
58 def newline: Text = copy(tx = FBreak :: tx, pos = 0.0, nl = nl + 1) |
|
59 def string(s: String, len: Double): Text = copy(tx = XML.Text(s) :: tx, pos = pos + len) |
|
60 def blanks(wd: Int): Text = string(Symbol.spaces(wd), wd.toDouble) |
|
61 def content: List[XML.Tree] = tx.reverse |
|
62 } |
|
63 |
82 private val margin_default = 76 |
64 private val margin_default = 76 |
83 |
65 |
84 def formatted(input: List[XML.Tree], margin: Int = margin_default): List[XML.Tree] = |
66 def formatted(input: List[XML.Tree], margin: Int = margin_default, |
|
67 metric: String => Double = (_.length.toDouble)): List[XML.Tree] = |
85 { |
68 { |
86 val breakgain = margin / 20 |
69 val breakgain = margin / 20 |
87 val emergencypos = margin / 2 |
70 val emergencypos = margin / 2 |
88 |
71 |
89 def format(trees: List[XML.Tree], blockin: Int, after: Int, text: Text): Text = |
72 def content_length(tree: XML.Tree): Double = |
|
73 tree match { |
|
74 case XML.Elem(_, _, body) => (0.0 /: body)(_ + content_length(_)) |
|
75 case XML.Text(s) => metric(s) |
|
76 } |
|
77 |
|
78 def breakdist(trees: List[XML.Tree], after: Double): Double = |
|
79 trees match { |
|
80 case Break(_) :: _ => 0.0 |
|
81 case FBreak :: _ => 0.0 |
|
82 case XML.Elem(_, _, body) :: ts => |
|
83 (0.0 /: body)(_ + content_length(_)) + breakdist(ts, after) |
|
84 case XML.Text(s) :: ts => metric(s) + breakdist(ts, after) |
|
85 case Nil => after |
|
86 } |
|
87 |
|
88 def forcenext(trees: List[XML.Tree]): List[XML.Tree] = |
|
89 trees match { |
|
90 case Nil => Nil |
|
91 case FBreak :: _ => trees |
|
92 case Break(_) :: ts => FBreak :: ts |
|
93 case t :: ts => t :: forcenext(ts) |
|
94 } |
|
95 |
|
96 def format(trees: List[XML.Tree], blockin: Double, after: Double, text: Text): Text = |
90 trees match { |
97 trees match { |
91 case Nil => text |
98 case Nil => text |
92 |
99 |
93 case Block(indent, body) :: ts => |
100 case Block(indent, body) :: ts => |
94 val pos1 = text.pos + indent |
101 val pos1 = text.pos + indent |
101 format(ts1, blockin, after, btext) |
108 format(ts1, blockin, after, btext) |
102 |
109 |
103 case Break(wd) :: ts => |
110 case Break(wd) :: ts => |
104 if (text.pos + wd <= (margin - breakdist(ts, after)).max(blockin + breakgain)) |
111 if (text.pos + wd <= (margin - breakdist(ts, after)).max(blockin + breakgain)) |
105 format(ts, blockin, after, text.blanks(wd)) |
112 format(ts, blockin, after, text.blanks(wd)) |
106 else format(ts, blockin, after, text.newline.blanks(blockin)) |
113 else format(ts, blockin, after, text.newline.blanks(blockin.toInt)) |
107 case FBreak :: ts => format(ts, blockin, after, text.newline.blanks(blockin)) |
114 case FBreak :: ts => format(ts, blockin, after, text.newline.blanks(blockin.toInt)) |
108 |
115 |
109 case XML.Elem(name, atts, body) :: ts => |
116 case XML.Elem(name, atts, body) :: ts => |
110 val btext = format(body, blockin, breakdist(ts, after), text.copy(tx = Nil)) |
117 val btext = format(body, blockin, breakdist(ts, after), text.copy(tx = Nil)) |
111 val ts1 = if (text.nl < btext.nl) forcenext(ts) else ts |
118 val ts1 = if (text.nl < btext.nl) forcenext(ts) else ts |
112 val btext1 = btext.copy(tx = XML.Elem(name, atts, btext.content) :: text.tx) |
119 val btext1 = btext.copy(tx = XML.Elem(name, atts, btext.content) :: text.tx) |
113 format(ts1, blockin, after, btext1) |
120 format(ts1, blockin, after, btext1) |
114 case XML.Text(s) :: ts => format(ts, blockin, after, text.string(s)) |
121 case XML.Text(s) :: ts => format(ts, blockin, after, text.string(s, metric(s))) |
115 } |
122 } |
116 format(input.flatMap(standard_format), 0, 0, Text()).content |
123 format(input.flatMap(standard_format), 0.0, 0.0, Text()).content |
117 } |
124 } |
118 |
125 |
119 def string_of(input: List[XML.Tree], margin: Int = margin_default): String = |
126 def string_of(input: List[XML.Tree], margin: Int = margin_default): String = |
120 formatted(input).iterator.flatMap(XML.content).mkString |
127 formatted(input).iterator.flatMap(XML.content).mkString |
121 |
128 |
126 { |
133 { |
127 def fmt(tree: XML.Tree): List[XML.Tree] = |
134 def fmt(tree: XML.Tree): List[XML.Tree] = |
128 tree match { |
135 tree match { |
129 case Block(_, body) => body.flatMap(fmt) |
136 case Block(_, body) => body.flatMap(fmt) |
130 case Break(wd) => List(XML.Text(Symbol.spaces(wd))) |
137 case Break(wd) => List(XML.Text(Symbol.spaces(wd))) |
131 case FBreak => List(XML.Text(" ")) |
138 case FBreak => List(XML.Text(Symbol.space)) |
132 case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(fmt))) |
139 case XML.Elem(name, atts, body) => List(XML.Elem(name, atts, body.flatMap(fmt))) |
133 case XML.Text(_) => List(tree) |
140 case XML.Text(_) => List(tree) |
134 } |
141 } |
135 input.flatMap(standard_format).flatMap(fmt) |
142 input.flatMap(standard_format).flatMap(fmt) |
136 } |
143 } |