| author | wenzelm | 
| Sat, 08 Jul 2023 19:28:26 +0200 | |
| changeset 78271 | c0dc000d2a40 | 
| parent 76975 | 5ba8cb258e75 | 
| child 83203 | 61277d1550d6 | 
| permissions | -rw-r--r-- | 
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 1 | /* Title: Pure/PIDE/markup_tree.scala | 
| 36676 | 2 | Author: Fabian Immler, TU Munich | 
| 3 | Author: Makarius | |
| 4 | ||
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 5 | Markup trees over nested / non-overlapping text ranges. | 
| 36676 | 6 | */ | 
| 34393 | 7 | |
| 34871 
e596a0b71f3c
incorporate "proofdocument" part into main Isabelle/Pure.jar -- except for html_panel.scala, which depends on external library (Lobo/Cobra browser);
 wenzelm parents: 
34760diff
changeset | 8 | package isabelle | 
| 34393 | 9 | |
| 34701 
80b0add08eef
IsabelleSideKickParser: incorporate former MarkupNode.markup2default_node, observe stopped flag;
 wenzelm parents: 
34676diff
changeset | 10 | |
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 11 | import scala.collection.immutable.SortedMap | 
| 49614 | 12 | import scala.collection.mutable | 
| 49467 | 13 | import scala.annotation.tailrec | 
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 14 | |
| 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 15 | |
| 75393 | 16 | object Markup_Tree {
 | 
| 50551 | 17 | /* construct trees */ | 
| 18 | ||
| 45456 | 19 | val empty: Markup_Tree = new Markup_Tree(Branches.empty) | 
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 20 | |
| 56743 | 21 | def merge(trees: List[Markup_Tree], range: Text.Range, elements: Markup.Elements): Markup_Tree = | 
| 73359 | 22 | trees.foldLeft(empty)(_.merge(_, range, elements)) | 
| 56299 
8201790fdeb9
more careful treatment of multiple command states (eval + prints): merge content that is actually required;
 wenzelm parents: 
55820diff
changeset | 23 | |
| 49467 | 24 | def merge_disjoint(trees: List[Markup_Tree]): Markup_Tree = | 
| 25 |     trees match {
 | |
| 26 | case Nil => empty | |
| 27 | case head :: tail => | |
| 28 | new Markup_Tree( | |
| 73359 | 29 |           tail.foldLeft(head.branches) {
 | 
| 49467 | 30 | case (branches, tree) => | 
| 73359 | 31 |               tree.branches.foldLeft(branches) {
 | 
| 49467 | 32 | case (bs, (r, entry)) => | 
| 73120 
c3589f2dff31
more informative errors: simplify diagnosis of spurious failures reported by users;
 wenzelm parents: 
72815diff
changeset | 33 | require(!bs.isDefinedAt(r), "cannot merge markup trees") | 
| 49467 | 34 | bs + (r -> entry) | 
| 35 | } | |
| 36 | }) | |
| 37 | } | |
| 38 | ||
| 50551 | 39 | |
| 40 | /* tree building blocks */ | |
| 41 | ||
| 75393 | 42 |   object Entry {
 | 
| 45473 | 43 | def apply(markup: Text.Markup, subtree: Markup_Tree): Entry = | 
| 55652 
33ad12ef79ff
eliminated somewhat pointless elements index (see also f793dd5d84b2, 2b7fed8c9c4ac): less memory and more speed (avoid linear "exists" of 19dffae33cde);
 wenzelm parents: 
55645diff
changeset | 44 | Entry(markup.range, List(markup.info), subtree) | 
| 45473 | 45 | } | 
| 46 | ||
| 45474 
f793dd5d84b2
index markup elements for more efficient cumulate/select operations;
 wenzelm parents: 
45473diff
changeset | 47 | sealed case class Entry( | 
| 
f793dd5d84b2
index markup elements for more efficient cumulate/select operations;
 wenzelm parents: 
45473diff
changeset | 48 | range: Text.Range, | 
| 
f793dd5d84b2
index markup elements for more efficient cumulate/select operations;
 wenzelm parents: 
45473diff
changeset | 49 | rev_markup: List[XML.Elem], | 
| 75393 | 50 | subtree: Markup_Tree | 
| 51 |   ) {
 | |
| 50552 
2b7fd8c9c4ac
maintain subtree_elements for improved performance of cumulate operator;
 wenzelm parents: 
50551diff
changeset | 52 | def markup: List[XML.Elem] = rev_markup.reverse | 
| 45474 
f793dd5d84b2
index markup elements for more efficient cumulate/select operations;
 wenzelm parents: 
45473diff
changeset | 53 | |
| 75393 | 54 |     def filter_markup(elements: Markup.Elements): List[XML.Elem] = {
 | 
| 55645 
561754277494
tuned -- remaining rev_markup is rather short after filter;
 wenzelm parents: 
55622diff
changeset | 55 | var result: List[XML.Elem] = Nil | 
| 60215 | 56 | for (elem <- rev_markup if elements(elem.name)) | 
| 55645 
561754277494
tuned -- remaining rev_markup is rather short after filter;
 wenzelm parents: 
55622diff
changeset | 57 | result ::= elem | 
| 72815 | 58 | result | 
| 55645 
561754277494
tuned -- remaining rev_markup is rather short after filter;
 wenzelm parents: 
55622diff
changeset | 59 | } | 
| 
561754277494
tuned -- remaining rev_markup is rather short after filter;
 wenzelm parents: 
55622diff
changeset | 60 | |
| 55652 
33ad12ef79ff
eliminated somewhat pointless elements index (see also f793dd5d84b2, 2b7fed8c9c4ac): less memory and more speed (avoid linear "exists" of 19dffae33cde);
 wenzelm parents: 
55645diff
changeset | 61 | def + (markup: Text.Markup): Entry = copy(rev_markup = markup.info :: rev_markup) | 
| 
33ad12ef79ff
eliminated somewhat pointless elements index (see also f793dd5d84b2, 2b7fed8c9c4ac): less memory and more speed (avoid linear "exists" of 19dffae33cde);
 wenzelm parents: 
55645diff
changeset | 62 | def \ (markup: Text.Markup): Entry = copy(subtree = subtree + markup) | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 63 | } | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 64 | |
| 75393 | 65 |   object Branches {
 | 
| 38578 | 66 | type T = SortedMap[Text.Range, Entry] | 
| 45456 | 67 | val empty: T = SortedMap.empty(Text.Range.Ordering) | 
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 68 | } | 
| 49466 | 69 | |
| 70 | ||
| 71 | /* XML representation */ | |
| 72 | ||
| 49650 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 73 | @tailrec private def strip_elems( | 
| 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 74 | elems: List[XML.Elem], body: XML.Body): (List[XML.Elem], XML.Body) = | 
| 49467 | 75 |     body match {
 | 
| 49650 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 76 | case List(XML.Wrapped_Elem(markup1, body1, body2)) => | 
| 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 77 | strip_elems(XML.Elem(markup1, body1) :: elems, body2) | 
| 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 78 | case List(XML.Elem(markup1, body1)) => | 
| 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 79 | strip_elems(XML.Elem(markup1, Nil) :: elems, body1) | 
| 
9fad6480300d
support for wrapped XML elements, which allows to preserve full markup tree information in to_XML/from_XML conversion;
 wenzelm parents: 
49614diff
changeset | 80 | case _ => (elems, body) | 
| 49467 | 81 | } | 
| 82 | ||
| 75393 | 83 | private def make_trees( | 
| 84 | acc: (Int, List[Markup_Tree]), | |
| 85 | tree: XML.Tree | |
| 86 |   ): (Int, List[Markup_Tree]) = {
 | |
| 87 | val (offset, markup_trees) = acc | |
| 49467 | 88 | |
| 75393 | 89 |     strip_elems(Nil, List(tree)) match {
 | 
| 90 | case (Nil, body) => | |
| 91 | (offset + XML.text_length(body), markup_trees) | |
| 49466 | 92 | |
| 75393 | 93 | case (elems, body) => | 
| 94 | val (end_offset, subtrees) = | |
| 95 | body.foldLeft((offset, List.empty[Markup_Tree]))(make_trees) | |
| 96 | if (offset == end_offset) acc | |
| 97 |         else {
 | |
| 98 | val range = Text.Range(offset, end_offset) | |
| 99 | val entry = Entry(range, elems, merge_disjoint(subtrees)) | |
| 100 | (end_offset, new Markup_Tree(Branches.empty, entry) :: markup_trees) | |
| 101 | } | |
| 49467 | 102 | } | 
| 75393 | 103 | } | 
| 49466 | 104 | |
| 49467 | 105 | def from_XML(body: XML.Body): Markup_Tree = | 
| 73361 | 106 | merge_disjoint(body.foldLeft((0, List.empty[Markup_Tree]))(make_trees)._2) | 
| 38479 
e628da370072
more efficient Markup_Tree, based on branches sorted by quasi-order;
 wenzelm parents: 
38478diff
changeset | 107 | } | 
| 34554 
7dc6c231da40
abs. stops, markup nodes depend on doc-version;
 immler@in.tum.de parents: 
34517diff
changeset | 108 | |
| 34393 | 109 | |
| 75393 | 110 | final class Markup_Tree private(val branches: Markup_Tree.Branches.T) {
 | 
| 49417 | 111 | import Markup_Tree._ | 
| 112 | ||
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 113 | private def this(branches: Markup_Tree.Branches.T, entry: Markup_Tree.Entry) = | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 114 | this(branches + (entry.range -> entry)) | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 115 | |
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 116 | private def overlapping(range: Text.Range): Branches.T = | 
| 56311 | 117 | if (branches.isEmpty || | 
| 56313 | 118 | (range.contains(branches.firstKey.start) && branches.lastKey.stop <= range.stop)) | 
| 56311 | 119 | branches | 
| 120 |     else {
 | |
| 121 | val start = Text.Range(range.start) | |
| 122 | val stop = Text.Range(range.stop) | |
| 123 | val bs = branches.range(start, stop) | |
| 124 |       branches.get(stop) match {
 | |
| 125 | case Some(end) if range overlaps end.range => bs + (end.range -> end) | |
| 126 | case _ => bs | |
| 127 | } | |
| 45457 | 128 | } | 
| 129 | ||
| 56307 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 130 | def restrict(range: Text.Range): Markup_Tree = | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 131 | new Markup_Tree(overlapping(range)) | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 132 | |
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 133 | def is_empty: Boolean = branches.isEmpty | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 134 | |
| 75393 | 135 |   def + (new_markup: Text.Markup): Markup_Tree = {
 | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 136 | val new_range = new_markup.range | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 137 | |
| 38578 | 138 |     branches.get(new_range) match {
 | 
| 45473 | 139 | case None => new Markup_Tree(branches, Entry(new_markup, empty)) | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 140 | case Some(entry) => | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 141 | if (entry.range == new_range) | 
| 50552 
2b7fd8c9c4ac
maintain subtree_elements for improved performance of cumulate operator;
 wenzelm parents: 
50551diff
changeset | 142 | new Markup_Tree(branches, entry + new_markup) | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 143 | else if (entry.range.contains(new_range)) | 
| 50552 
2b7fd8c9c4ac
maintain subtree_elements for improved performance of cumulate operator;
 wenzelm parents: 
50551diff
changeset | 144 | new Markup_Tree(branches, entry \ new_markup) | 
| 38578 | 145 | else if (new_range.contains(branches.head._1) && new_range.contains(branches.last._1)) | 
| 45473 | 146 | new Markup_Tree(Branches.empty, Entry(new_markup, this)) | 
| 38482 
7b6ee937b75f
tuned Markup_Tree, using SortedMap more carefully;
 wenzelm parents: 
38479diff
changeset | 147 |         else {
 | 
| 45457 | 148 | val body = overlapping(new_range) | 
| 49607 
b610c0d52bfa
updated to consolidated SortedMap in scala-2.9.x;
 wenzelm parents: 
49469diff
changeset | 149 | if (body.forall(e => new_range.contains(e._1))) | 
| 
b610c0d52bfa
updated to consolidated SortedMap in scala-2.9.x;
 wenzelm parents: 
49469diff
changeset | 150 | new Markup_Tree(branches -- body.keys, Entry(new_markup, new Markup_Tree(body))) | 
| 49608 | 151 |           else {
 | 
| 58463 | 152 |             Output.warning("Ignored overlapping markup information: " + new_markup + "\n" +
 | 
| 71383 | 153 |               body.filter(e => !new_range.contains(e._1)).values.mkString("\n"))
 | 
| 38482 
7b6ee937b75f
tuned Markup_Tree, using SortedMap more carefully;
 wenzelm parents: 
38479diff
changeset | 154 | this | 
| 
7b6ee937b75f
tuned Markup_Tree, using SortedMap more carefully;
 wenzelm parents: 
38479diff
changeset | 155 | } | 
| 
7b6ee937b75f
tuned Markup_Tree, using SortedMap more carefully;
 wenzelm parents: 
38479diff
changeset | 156 | } | 
| 34703 | 157 | } | 
| 158 | } | |
| 159 | ||
| 75393 | 160 |   def merge(other: Markup_Tree, root_range: Text.Range, elements: Markup.Elements): Markup_Tree = {
 | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 161 | def merge_trees(tree1: Markup_Tree, tree2: Markup_Tree): Markup_Tree = | 
| 73359 | 162 |       tree2.branches.foldLeft(tree1) {
 | 
| 163 | case (tree, (range, entry)) => | |
| 164 | if (!range.overlaps(root_range)) tree | |
| 165 |           else {
 | |
| 166 |             entry.filter_markup(elements).foldLeft(merge_trees(tree, entry.subtree)) {
 | |
| 167 | case (t, elem) => t + Text.Info(range, elem) | |
| 168 | } | |
| 169 | } | |
| 170 | } | |
| 49614 | 171 | |
| 56307 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 172 | if (this eq other) this | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 173 |     else {
 | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 174 | val tree1 = this.restrict(root_range) | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 175 | val tree2 = other.restrict(root_range) | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 176 | if (tree1.is_empty) tree2 | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 177 | else merge_trees(tree1, tree2) | 
| 
2bdf8261677a
more frugal merge of markup trees: restrict to expected range before checking trivial cases, e.g. relevant for sporadic warnings (eval_exec) within big ML reports (print_exec);
 wenzelm parents: 
56302diff
changeset | 178 | } | 
| 49614 | 179 | } | 
| 180 | ||
| 75393 | 181 | def cumulate[A]( | 
| 182 | root_range: Text.Range, | |
| 183 | root_info: A, | |
| 184 | elements: Markup.Elements, | |
| 185 | result: (A, Text.Markup) => Option[A] | |
| 186 |   ): List[Text.Info[A]] = {
 | |
| 187 |     def results(x: A, entry: Entry): Option[A] = {
 | |
| 52889 
ea3338812e67
more tight interface for markup cumulate/select: avoid duplicate application, allow to defer decision about definedness;
 wenzelm parents: 
52642diff
changeset | 188 | var y = x | 
| 
ea3338812e67
more tight interface for markup cumulate/select: avoid duplicate application, allow to defer decision about definedness;
 wenzelm parents: 
52642diff
changeset | 189 | var changed = false | 
| 
ea3338812e67
more tight interface for markup cumulate/select: avoid duplicate application, allow to defer decision about definedness;
 wenzelm parents: 
52642diff
changeset | 190 |       for {
 | 
| 55645 
561754277494
tuned -- remaining rev_markup is rather short after filter;
 wenzelm parents: 
55622diff
changeset | 191 | elem <- entry.filter_markup(elements) | 
| 55620 | 192 | y1 <- result(y, Text.Info(entry.range, elem)) | 
| 52889 
ea3338812e67
more tight interface for markup cumulate/select: avoid duplicate application, allow to defer decision about definedness;
 wenzelm parents: 
52642diff
changeset | 193 |       } { y = y1; changed = true }
 | 
| 50552 
2b7fd8c9c4ac
maintain subtree_elements for improved performance of cumulate operator;
 wenzelm parents: 
50551diff
changeset | 194 | if (changed) Some(y) else None | 
| 
2b7fd8c9c4ac
maintain subtree_elements for improved performance of cumulate operator;
 wenzelm parents: 
50551diff
changeset | 195 | } | 
| 45467 | 196 | |
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 197 | def traverse( | 
| 45459 | 198 | last: Text.Offset, | 
| 75393 | 199 | stack: List[(Text.Info[A], List[(Text.Range, Entry)])] | 
| 200 |     ): List[Text.Info[A]] = {
 | |
| 45459 | 201 |       stack match {
 | 
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 202 | case (parent, (range, entry) :: more) :: rest => | 
| 45467 | 203 | val subrange = range.restrict(root_range) | 
| 55652 
33ad12ef79ff
eliminated somewhat pointless elements index (see also f793dd5d84b2, 2b7fed8c9c4ac): less memory and more speed (avoid linear "exists" of 19dffae33cde);
 wenzelm parents: 
55645diff
changeset | 204 | val subtree = entry.subtree.overlapping(subrange).toList | 
| 45459 | 205 | val start = subrange.start | 
| 206 | ||
| 55652 
33ad12ef79ff
eliminated somewhat pointless elements index (see also f793dd5d84b2, 2b7fed8c9c4ac): less memory and more speed (avoid linear "exists" of 19dffae33cde);
 wenzelm parents: 
55645diff
changeset | 207 |           results(parent.info, entry) match {
 | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 208 | case Some(res) => | 
| 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 209 | val next = Text.Info(subrange, res) | 
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 210 | val nexts = traverse(start, (next, subtree) :: (parent, more) :: rest) | 
| 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 211 | if (last < start) parent.restrict(Text.Range(last, start)) :: nexts | 
| 45469 
25306d92f4ad
refined Markup_Tree implementation: stacked markup within each entry;
 wenzelm parents: 
45468diff
changeset | 212 | else nexts | 
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 213 | case None => traverse(last, (parent, subtree ::: more) :: rest) | 
| 45459 | 214 | } | 
| 215 | ||
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 216 | case (parent, Nil) :: rest => | 
| 45459 | 217 | val stop = parent.range.stop | 
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 218 | val nexts = traverse(stop, rest) | 
| 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 219 | if (last < stop) parent.restrict(Text.Range(last, stop)) :: nexts | 
| 45459 | 220 | else nexts | 
| 221 | ||
| 222 | case Nil => | |
| 45467 | 223 | val stop = root_range.stop | 
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 224 | if (last < stop) List(Text.Info(Text.Range(last, stop), root_info)) | 
| 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 225 | else Nil | 
| 45459 | 226 | } | 
| 227 | } | |
| 52900 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 228 | traverse(root_range.start, | 
| 
d29bf6db8a2d
more elementary list structures for markup tree traversal;
 wenzelm parents: 
52889diff
changeset | 229 | List((Text.Info(root_range, root_info), overlapping(root_range).toList))) | 
| 45459 | 230 | } | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 231 | |
| 75393 | 232 |   def to_XML(root_range: Text.Range, text: CharSequence, elements: Markup.Elements): XML.Body = {
 | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 233 | def make_text(start: Text.Offset, stop: Text.Offset): XML.Body = | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 234 | if (start == stop) Nil | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 235 | else List(XML.Text(text.subSequence(start, stop).toString)) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 236 | |
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 237 | def make_elems(rev_markups: List[XML.Elem], body: XML.Body): XML.Body = | 
| 73359 | 238 |       rev_markups.foldLeft(body) {
 | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 239 | case (b, elem) => | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 240 | if (!elements(elem.name)) b | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 241 | else if (elem.body.isEmpty) List(XML.Elem(elem.markup, b)) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 242 | else List(XML.Wrapped_Elem(elem.markup, elem.body, b)) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 243 | } | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 244 | |
| 76975 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 245 | @tailrec def normal_body(trees: List[XML.Tree], res: XML.Body = Nil): XML.Body = | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 246 | if (trees.isEmpty) res.reverse | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 247 |       else {
 | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 248 | val (texts, trees1) = Library.take_prefix[XML.Tree](_.isInstanceOf[XML.Text], trees) | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 249 | val (elems, trees2) = Library.take_prefix[XML.Tree](_.isInstanceOf[XML.Elem], trees1) | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 250 |         val res1 = XML.content(texts) match { case "" => res case txt => XML.Text(txt) :: res }
 | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 251 |         val res2 = elems.foldLeft(res1)({ case (ts, t) => t :: ts })
 | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 252 | normal_body(trees2, res2) | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 253 | } | 
| 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 254 | |
| 75393 | 255 | def make_body( | 
| 256 | elem_range: Text.Range, | |
| 257 | elem_markup: List[XML.Elem], | |
| 258 | entries: Branches.T | |
| 259 |     ) : XML.Body = {
 | |
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 260 | val body = new mutable.ListBuffer[XML.Tree] | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 261 | var last = elem_range.start | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 262 |       for ((range, entry) <- entries) {
 | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 263 | val subrange = range.restrict(elem_range) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 264 | body ++= make_text(last, subrange.start) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 265 | body ++= make_body(subrange, entry.rev_markup, entry.subtree.overlapping(subrange)) | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 266 | last = subrange.stop | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 267 | } | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 268 | body ++= make_text(last, elem_range.stop) | 
| 76975 
5ba8cb258e75
proper normal form of adjacent XML.Text, notably for Bibtex.update_cite;
 wenzelm parents: 
75659diff
changeset | 269 | make_elems(elem_markup, normal_body(body.toList)) | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 270 | } | 
| 75659 
9bd92ac9328f
more robust Scala 3 indentation, for the sake of IntelliJ IDEA;
 wenzelm parents: 
75393diff
changeset | 271 | make_body(root_range, Nil, overlapping(root_range)) | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 272 | } | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 273 | |
| 57912 | 274 | override def toString: String = | 
| 56301 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 275 |     branches.toList.map(_._2) match {
 | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 276 | case Nil => "Empty" | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 277 |       case list => list.mkString("Tree(", ",", ")")
 | 
| 
1da7b4c33db9
more frugal merge of markup trees: filter wrt. subsequent query;
 wenzelm parents: 
56300diff
changeset | 278 | } | 
| 34717 
3f32e08bbb6c
sidekick root data: set buffer length to avoid crash of initial caret move;
 wenzelm parents: 
34708diff
changeset | 279 | } |