wenzelm@38479: /* Title: Pure/PIDE/markup_tree.scala wenzelm@45673: Module: PIDE wenzelm@36676: Author: Fabian Immler, TU Munich wenzelm@36676: Author: Makarius wenzelm@36676: wenzelm@38479: Markup trees over nested / non-overlapping text ranges. wenzelm@36676: */ immler@34393: wenzelm@34871: package isabelle immler@34393: wenzelm@34701: wenzelm@38479: import scala.collection.immutable.SortedMap wenzelm@49614: import scala.collection.mutable wenzelm@49467: import scala.annotation.tailrec wenzelm@38479: wenzelm@38479: wenzelm@38479: object Markup_Tree wenzelm@38479: { wenzelm@50551: /* construct trees */ wenzelm@50551: wenzelm@45456: val empty: Markup_Tree = new Markup_Tree(Branches.empty) wenzelm@38479: wenzelm@49467: def merge_disjoint(trees: List[Markup_Tree]): Markup_Tree = wenzelm@49467: trees match { wenzelm@49467: case Nil => empty wenzelm@49467: case head :: tail => wenzelm@49467: new Markup_Tree( wenzelm@49467: (head.branches /: tail) { wenzelm@49467: case (branches, tree) => wenzelm@49467: (branches /: tree.branches) { wenzelm@49467: case (bs, (r, entry)) => wenzelm@49467: require(!bs.isDefinedAt(r)) wenzelm@49467: bs + (r -> entry) wenzelm@49467: } wenzelm@49467: }) wenzelm@49467: } wenzelm@49467: wenzelm@50551: wenzelm@50551: /* tree building blocks */ wenzelm@50551: wenzelm@50551: object Elements wenzelm@50551: { wenzelm@50551: val empty = new Elements(Set.empty) wenzelm@50551: } wenzelm@50551: wenzelm@50551: final class Elements private(private val rep: Set[String]) wenzelm@50551: { wenzelm@50551: def contains(name: String): Boolean = rep.contains(name) wenzelm@50551: wenzelm@50551: def + (name: String): Elements = wenzelm@50551: if (contains(name)) this wenzelm@50551: else new Elements(rep + name) wenzelm@50551: wenzelm@50551: def + (elem: XML.Elem): Elements = this + elem.markup.name wenzelm@50551: def ++ (elems: Iterable[XML.Elem]): Elements = (this /: elems.iterator)(_ + _) wenzelm@50551: wenzelm@50551: def ++ (other: Elements): Elements = wenzelm@50551: if (this eq other) this wenzelm@50551: else if (rep.isEmpty) other wenzelm@50551: else (this /: other.rep)(_ + _) wenzelm@50551: } wenzelm@50551: wenzelm@45473: object Entry wenzelm@45473: { wenzelm@45473: def apply(markup: Text.Markup, subtree: Markup_Tree): Entry = wenzelm@50552: Entry(markup.range, List(markup.info), Elements.empty + markup.info, wenzelm@50552: subtree, subtree.make_elements) wenzelm@49469: wenzelm@49469: def apply(range: Text.Range, rev_markups: List[XML.Elem], subtree: Markup_Tree): Entry = wenzelm@50552: Entry(range, rev_markups, Elements.empty ++ rev_markups, wenzelm@50552: subtree, subtree.make_elements) wenzelm@45473: } wenzelm@45473: wenzelm@45474: sealed case class Entry( wenzelm@45474: range: Text.Range, wenzelm@45474: rev_markup: List[XML.Elem], wenzelm@50551: elements: Elements, wenzelm@50552: subtree: Markup_Tree, wenzelm@50552: subtree_elements: Elements) wenzelm@45469: { wenzelm@50552: def markup: List[XML.Elem] = rev_markup.reverse wenzelm@45474: wenzelm@50552: def + (markup: Text.Markup): Entry = wenzelm@50552: copy(rev_markup = markup.info :: rev_markup, elements = elements + markup.info) wenzelm@50552: wenzelm@50552: def \ (markup: Text.Markup): Entry = wenzelm@50552: copy(subtree = subtree + markup, subtree_elements = subtree_elements + markup.info) wenzelm@45469: } wenzelm@45469: wenzelm@38479: object Branches wenzelm@38479: { wenzelm@38578: type T = SortedMap[Text.Range, Entry] wenzelm@45456: val empty: T = SortedMap.empty(Text.Range.Ordering) wenzelm@38479: } wenzelm@49466: wenzelm@49466: wenzelm@49466: /* XML representation */ wenzelm@49466: wenzelm@49650: @tailrec private def strip_elems( wenzelm@49650: elems: List[XML.Elem], body: XML.Body): (List[XML.Elem], XML.Body) = wenzelm@49467: body match { wenzelm@49650: case List(XML.Wrapped_Elem(markup1, body1, body2)) => wenzelm@49650: strip_elems(XML.Elem(markup1, body1) :: elems, body2) wenzelm@49650: case List(XML.Elem(markup1, body1)) => wenzelm@49650: strip_elems(XML.Elem(markup1, Nil) :: elems, body1) wenzelm@49650: case _ => (elems, body) wenzelm@49467: } wenzelm@49467: wenzelm@49467: private def make_trees(acc: (Int, List[Markup_Tree]), tree: XML.Tree): (Int, List[Markup_Tree]) = wenzelm@49467: { wenzelm@49467: val (offset, markup_trees) = acc wenzelm@49467: wenzelm@49467: strip_elems(Nil, List(tree)) match { wenzelm@49467: case (Nil, body) => wenzelm@49467: (offset + XML.text_length(body), markup_trees) wenzelm@49466: wenzelm@49469: case (elems, body) => wenzelm@49469: val (end_offset, subtrees) = ((offset, Nil: List[Markup_Tree]) /: body)(make_trees) wenzelm@50642: if (offset == end_offset) acc wenzelm@50642: else { wenzelm@50642: val range = Text.Range(offset, end_offset) wenzelm@50642: val entry = Entry(range, elems, merge_disjoint(subtrees)) wenzelm@50642: (end_offset, new Markup_Tree(Branches.empty, entry) :: markup_trees) wenzelm@50642: } wenzelm@49466: } wenzelm@49467: } wenzelm@49466: wenzelm@49467: def from_XML(body: XML.Body): Markup_Tree = wenzelm@49467: merge_disjoint(((0, Nil: List[Markup_Tree]) /: body)(make_trees)._2) wenzelm@38479: } immler@34554: immler@34393: wenzelm@51618: final class Markup_Tree private(val branches: Markup_Tree.Branches.T) wenzelm@34717: { wenzelm@49417: import Markup_Tree._ wenzelm@49417: wenzelm@45469: private def this(branches: Markup_Tree.Branches.T, entry: Markup_Tree.Entry) = wenzelm@45469: this(branches + (entry.range -> entry)) wenzelm@45469: wenzelm@38563: override def toString = wenzelm@38563: branches.toList.map(_._2) match { wenzelm@38563: case Nil => "Empty" wenzelm@38563: case list => list.mkString("Tree(", ",", ")") wenzelm@38563: } wenzelm@38563: wenzelm@45469: private def overlapping(range: Text.Range): Branches.T = wenzelm@45457: { wenzelm@45457: val start = Text.Range(range.start) wenzelm@45457: val stop = Text.Range(range.stop) wenzelm@45457: val bs = branches.range(start, stop) wenzelm@45457: branches.get(stop) match { wenzelm@45469: case Some(end) if range overlaps end.range => bs + (end.range -> end) wenzelm@45457: case _ => bs wenzelm@45457: } wenzelm@45457: } wenzelm@45457: wenzelm@50552: def make_elements: Elements = wenzelm@50552: (Elements.empty /: branches)( wenzelm@50552: { case (elements, (_, entry)) => elements ++ entry.subtree_elements ++ entry.elements }) wenzelm@50552: wenzelm@45469: def + (new_markup: Text.Markup): Markup_Tree = wenzelm@34703: { wenzelm@45469: val new_range = new_markup.range wenzelm@45469: wenzelm@38578: branches.get(new_range) match { wenzelm@45473: case None => new Markup_Tree(branches, Entry(new_markup, empty)) wenzelm@45469: case Some(entry) => wenzelm@45469: if (entry.range == new_range) wenzelm@50552: new Markup_Tree(branches, entry + new_markup) wenzelm@45469: else if (entry.range.contains(new_range)) wenzelm@50552: new Markup_Tree(branches, entry \ new_markup) wenzelm@38578: else if (new_range.contains(branches.head._1) && new_range.contains(branches.last._1)) wenzelm@45473: new Markup_Tree(Branches.empty, Entry(new_markup, this)) wenzelm@38482: else { wenzelm@45457: val body = overlapping(new_range) wenzelm@49607: if (body.forall(e => new_range.contains(e._1))) wenzelm@49607: new Markup_Tree(branches -- body.keys, Entry(new_markup, new Markup_Tree(body))) wenzelm@49608: else { wenzelm@55618: System.err.println("Ignored overlapping markup information: " + new_markup + wenzelm@48762: body.filter(e => !new_range.contains(e._1)).mkString("\n")) wenzelm@38482: this wenzelm@38482: } wenzelm@38482: } wenzelm@34703: } wenzelm@34703: } wenzelm@34703: wenzelm@52642: def ++ (other: Markup_Tree): Markup_Tree = wenzelm@52642: (this /: other.branches)({ case (tree, (range, entry)) => wenzelm@52642: ((tree ++ entry.subtree) /: entry.markup)({ case (t, elem) => t + Text.Info(range, elem) }) }) wenzelm@52642: wenzelm@49614: def to_XML(root_range: Text.Range, text: CharSequence, filter: XML.Elem => Boolean): XML.Body = wenzelm@49614: { wenzelm@49614: def make_text(start: Text.Offset, stop: Text.Offset): XML.Body = wenzelm@49614: if (start == stop) Nil wenzelm@49614: else List(XML.Text(text.subSequence(start, stop).toString)) wenzelm@49614: wenzelm@49614: def make_elems(rev_markups: List[XML.Elem], body: XML.Body): XML.Body = wenzelm@49614: (body /: rev_markups) { wenzelm@49650: case (b, elem) => wenzelm@49650: if (!filter(elem)) b wenzelm@49650: else if (elem.body.isEmpty) List(XML.Elem(elem.markup, b)) wenzelm@49650: else List(XML.Wrapped_Elem(elem.markup, elem.body, b)) wenzelm@49614: } wenzelm@49614: wenzelm@49614: def make_body(elem_range: Text.Range, elem_markup: List[XML.Elem], entries: Branches.T) wenzelm@49614: : XML.Body = wenzelm@49614: { wenzelm@49614: val body = new mutable.ListBuffer[XML.Tree] wenzelm@49614: var last = elem_range.start wenzelm@49614: for ((range, entry) <- entries) { wenzelm@49614: val subrange = range.restrict(elem_range) wenzelm@49614: body ++= make_text(last, subrange.start) wenzelm@49614: body ++= make_body(subrange, entry.rev_markup, entry.subtree.overlapping(subrange)) wenzelm@49614: last = subrange.stop wenzelm@49614: } wenzelm@49614: body ++= make_text(last, elem_range.stop) wenzelm@49614: make_elems(elem_markup, body.toList) wenzelm@49614: } wenzelm@49614: make_body(root_range, Nil, overlapping(root_range)) wenzelm@49614: } wenzelm@49614: wenzelm@49614: def to_XML(text: CharSequence): XML.Body = wenzelm@49614: to_XML(Text.Range(0, text.length), text, (_: XML.Elem) => true) wenzelm@49614: wenzelm@46178: def cumulate[A](root_range: Text.Range, root_info: A, result_elements: Option[Set[String]], wenzelm@52900: result: (A, Text.Markup) => Option[A]): List[Text.Info[A]] = wenzelm@45459: { wenzelm@50551: val notable: Elements => Boolean = wenzelm@50551: result_elements match { wenzelm@50551: case Some(res) => (elements: Elements) => res.exists(elements.contains) wenzelm@50551: case None => (elements: Elements) => true wenzelm@50551: } wenzelm@50551: wenzelm@46178: def results(x: A, entry: Entry): Option[A] = wenzelm@50552: { wenzelm@52889: var y = x wenzelm@52889: var changed = false wenzelm@52889: for { wenzelm@52889: info <- entry.rev_markup // FIXME proper cumulation order (including status markup) (!?) wenzelm@52889: y1 <- result(y, Text.Info(entry.range, info)) wenzelm@52889: } { y = y1; changed = true } wenzelm@50552: if (changed) Some(y) else None wenzelm@50552: } wenzelm@45467: wenzelm@52900: def traverse( wenzelm@45459: last: Text.Offset, wenzelm@52900: stack: List[(Text.Info[A], List[(Text.Range, Entry)])]): List[Text.Info[A]] = wenzelm@45459: { wenzelm@45459: stack match { wenzelm@52900: case (parent, (range, entry) :: more) :: rest => wenzelm@45467: val subrange = range.restrict(root_range) wenzelm@50552: val subtree = wenzelm@50552: if (notable(entry.subtree_elements)) wenzelm@52900: entry.subtree.overlapping(subrange).toList wenzelm@52900: else Nil wenzelm@45459: val start = subrange.start wenzelm@45459: wenzelm@50552: (if (notable(entry.elements)) results(parent.info, entry) else None) match { wenzelm@45469: case Some(res) => wenzelm@45469: val next = Text.Info(subrange, res) wenzelm@52900: val nexts = traverse(start, (next, subtree) :: (parent, more) :: rest) wenzelm@52900: if (last < start) parent.restrict(Text.Range(last, start)) :: nexts wenzelm@45469: else nexts wenzelm@52900: case None => traverse(last, (parent, subtree ::: more) :: rest) wenzelm@45459: } wenzelm@45459: wenzelm@52900: case (parent, Nil) :: rest => wenzelm@45459: val stop = parent.range.stop wenzelm@52900: val nexts = traverse(stop, rest) wenzelm@52900: if (last < stop) parent.restrict(Text.Range(last, stop)) :: nexts wenzelm@45459: else nexts wenzelm@45459: wenzelm@45459: case Nil => wenzelm@45467: val stop = root_range.stop wenzelm@52900: if (last < stop) List(Text.Info(Text.Range(last, stop), root_info)) wenzelm@52900: else Nil wenzelm@45459: } wenzelm@45459: } wenzelm@52900: traverse(root_range.start, wenzelm@52900: List((Text.Info(root_range, root_info), overlapping(root_range).toList))) wenzelm@45459: } wenzelm@34717: } immler@34514: