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@56743: def merge(trees: List[Markup_Tree], range: Text.Range, elements: Markup.Elements): Markup_Tree = wenzelm@56301: (empty /: trees)(_.merge(_, range, elements)) wenzelm@56299: 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@45473: object Entry wenzelm@45473: { wenzelm@45473: def apply(markup: Text.Markup, subtree: Markup_Tree): Entry = wenzelm@55652: Entry(markup.range, List(markup.info), subtree) wenzelm@45473: } wenzelm@45473: wenzelm@45474: sealed case class Entry( wenzelm@45474: range: Text.Range, wenzelm@45474: rev_markup: List[XML.Elem], wenzelm@55652: subtree: Markup_Tree) wenzelm@45469: { wenzelm@50552: def markup: List[XML.Elem] = rev_markup.reverse wenzelm@45474: wenzelm@56743: def filter_markup(elements: Markup.Elements): List[XML.Elem] = wenzelm@55645: { wenzelm@55645: var result: List[XML.Elem] = Nil wenzelm@55820: for { elem <- rev_markup; if (elements(elem.name)) } wenzelm@55645: result ::= elem wenzelm@55645: result.toList wenzelm@55645: } wenzelm@55645: wenzelm@55652: def + (markup: Text.Markup): Entry = copy(rev_markup = markup.info :: rev_markup) wenzelm@55652: def \ (markup: Text.Markup): Entry = copy(subtree = subtree + markup) 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@45469: private def overlapping(range: Text.Range): Branches.T = wenzelm@56311: if (branches.isEmpty || wenzelm@56313: (range.contains(branches.firstKey.start) && branches.lastKey.stop <= range.stop)) wenzelm@56311: branches wenzelm@56311: else { wenzelm@56311: val start = Text.Range(range.start) wenzelm@56311: val stop = Text.Range(range.stop) wenzelm@56311: val bs = branches.range(start, stop) wenzelm@56311: branches.get(stop) match { wenzelm@56311: case Some(end) if range overlaps end.range => bs + (end.range -> end) wenzelm@56311: case _ => bs wenzelm@56311: } wenzelm@45457: } wenzelm@45457: wenzelm@56307: def restrict(range: Text.Range): Markup_Tree = wenzelm@56307: new Markup_Tree(overlapping(range)) wenzelm@56307: wenzelm@56307: def is_empty: Boolean = branches.isEmpty wenzelm@56307: 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@56782: Output.warning("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@56743: def merge(other: Markup_Tree, root_range: Text.Range, elements: Markup.Elements): Markup_Tree = wenzelm@49614: { wenzelm@56301: def merge_trees(tree1: Markup_Tree, tree2: Markup_Tree): Markup_Tree = wenzelm@56307: (tree1 /: tree2.branches)( wenzelm@56307: { case (tree, (range, entry)) => wenzelm@56307: if (!range.overlaps(root_range)) tree wenzelm@56307: else wenzelm@56307: (merge_trees(tree, entry.subtree) /: entry.filter_markup(elements))( wenzelm@56307: { case (t, elem) => t + Text.Info(range, elem) }) wenzelm@56307: }) wenzelm@49614: wenzelm@56307: if (this eq other) this wenzelm@56307: else { wenzelm@56307: val tree1 = this.restrict(root_range) wenzelm@56307: val tree2 = other.restrict(root_range) wenzelm@56307: if (tree1.is_empty) tree2 wenzelm@56307: else merge_trees(tree1, tree2) wenzelm@56307: } wenzelm@49614: } wenzelm@49614: wenzelm@56743: def cumulate[A](root_range: Text.Range, root_info: A, elements: Markup.Elements, wenzelm@52900: result: (A, Text.Markup) => Option[A]): List[Text.Info[A]] = wenzelm@45459: { 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@55645: elem <- entry.filter_markup(elements) wenzelm@55620: y1 <- result(y, Text.Info(entry.range, elem)) 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@55652: val subtree = entry.subtree.overlapping(subrange).toList wenzelm@45459: val start = subrange.start wenzelm@45459: wenzelm@55652: results(parent.info, entry) 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@56301: wenzelm@56743: def to_XML(root_range: Text.Range, text: CharSequence, elements: Markup.Elements): XML.Body = wenzelm@56301: { wenzelm@56301: def make_text(start: Text.Offset, stop: Text.Offset): XML.Body = wenzelm@56301: if (start == stop) Nil wenzelm@56301: else List(XML.Text(text.subSequence(start, stop).toString)) wenzelm@56301: wenzelm@56301: def make_elems(rev_markups: List[XML.Elem], body: XML.Body): XML.Body = wenzelm@56301: (body /: rev_markups) { wenzelm@56301: case (b, elem) => wenzelm@56301: if (!elements(elem.name)) b wenzelm@56301: else if (elem.body.isEmpty) List(XML.Elem(elem.markup, b)) wenzelm@56301: else List(XML.Wrapped_Elem(elem.markup, elem.body, b)) wenzelm@56301: } wenzelm@56301: wenzelm@56301: def make_body(elem_range: Text.Range, elem_markup: List[XML.Elem], entries: Branches.T) wenzelm@56301: : XML.Body = wenzelm@56301: { wenzelm@56301: val body = new mutable.ListBuffer[XML.Tree] wenzelm@56301: var last = elem_range.start wenzelm@56301: for ((range, entry) <- entries) { wenzelm@56301: val subrange = range.restrict(elem_range) wenzelm@56301: body ++= make_text(last, subrange.start) wenzelm@56301: body ++= make_body(subrange, entry.rev_markup, entry.subtree.overlapping(subrange)) wenzelm@56301: last = subrange.stop wenzelm@56301: } wenzelm@56301: body ++= make_text(last, elem_range.stop) wenzelm@56301: make_elems(elem_markup, body.toList) wenzelm@56301: } wenzelm@56301: make_body(root_range, Nil, overlapping(root_range)) wenzelm@56301: } wenzelm@56301: wenzelm@57912: override def toString: String = wenzelm@56301: branches.toList.map(_._2) match { wenzelm@56301: case Nil => "Empty" wenzelm@56301: case list => list.mkString("Tree(", ",", ")") wenzelm@56301: } wenzelm@34717: } immler@34514: