wenzelm@38479: /* Title: Pure/PIDE/markup_tree.scala 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@34701: import javax.swing.tree.DefaultMutableTreeNode wenzelm@34701: wenzelm@38479: import scala.collection.immutable.SortedMap wenzelm@38479: import scala.collection.mutable wenzelm@38479: import scala.annotation.tailrec wenzelm@38479: wenzelm@38479: wenzelm@38479: object Markup_Tree wenzelm@38479: { wenzelm@38564: case class Node[A](val range: Text.Range, val info: A) wenzelm@38482: { wenzelm@38564: def contains[B](that: Node[B]): Boolean = this.range contains that.range wenzelm@38564: def restrict(r: Text.Range): Node[A] = Node(range.restrict(r), info) wenzelm@38482: } wenzelm@38479: wenzelm@38479: wenzelm@38566: /* branches sorted by quasi-order -- overlapping ranges appear as equivalent */ wenzelm@38479: wenzelm@38479: object Branches wenzelm@38479: { wenzelm@38564: type Entry = (Node[Any], Markup_Tree) wenzelm@38564: type T = SortedMap[Node[Any], Entry] wenzelm@38479: wenzelm@38564: val empty = SortedMap.empty[Node[Any], Entry](new scala.math.Ordering[Node[Any]] wenzelm@38479: { wenzelm@38564: def compare(x: Node[Any], y: Node[Any]): Int = x.range compare y.range wenzelm@38479: }) wenzelm@38562: wenzelm@38562: def update(branches: T, entry: Entry): T = branches + (entry._1 -> entry) wenzelm@38562: wenzelm@38562: def overlapping(range: Text.Range, branches: T): T = wenzelm@38566: { wenzelm@38568: val start = Node[Any](Text.Range(range.start), Nil) wenzelm@38568: val stop = Node[Any](Text.Range(range.stop), Nil) wenzelm@38566: branches.get(stop) match { wenzelm@38566: case Some(end) if range overlaps end._1.range => wenzelm@38566: update(branches.range(start, stop), end) wenzelm@38566: case _ => branches.range(start, stop) wenzelm@38566: } wenzelm@38566: } wenzelm@38479: } wenzelm@38479: wenzelm@38479: val empty = new Markup_Tree(Branches.empty) wenzelm@38479: } immler@34554: immler@34393: wenzelm@38479: case class Markup_Tree(val branches: Markup_Tree.Branches.T) wenzelm@34717: { wenzelm@38479: import Markup_Tree._ immler@34557: 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@38564: def + (new_node: Node[Any]): Markup_Tree = wenzelm@34703: { wenzelm@38482: branches.get(new_node) match { wenzelm@38482: case None => wenzelm@38479: new Markup_Tree(Branches.update(branches, new_node -> empty)) wenzelm@38482: case Some((node, subtree)) => wenzelm@38482: if (node.range != new_node.range && node.contains(new_node)) wenzelm@38482: new Markup_Tree(Branches.update(branches, node -> (subtree + new_node))) wenzelm@38482: else if (new_node.contains(branches.head._1) && new_node.contains(branches.last._1)) wenzelm@38482: new Markup_Tree(Branches.update(Branches.empty, (new_node -> this))) wenzelm@38482: else { wenzelm@38562: val body = Branches.overlapping(new_node.range, branches) wenzelm@38562: if (body.forall(e => new_node.contains(e._1))) { wenzelm@38562: val rest = (branches /: body) { case (bs, (e, _)) => bs - e } wenzelm@38562: new Markup_Tree(Branches.update(rest, new_node -> new Markup_Tree(body))) wenzelm@38482: } wenzelm@38482: else { // FIXME split markup!? wenzelm@38482: System.err.println("Ignored overlapping markup: " + new_node) wenzelm@38482: this wenzelm@38482: } wenzelm@38482: } wenzelm@34703: } wenzelm@34703: } wenzelm@34703: wenzelm@38479: // FIXME depth-first with result markup stack wenzelm@38479: // FIXME projection to given range wenzelm@38564: def flatten(parent: Node[Any]): List[Node[Any]] = wenzelm@34717: { wenzelm@38564: val result = new mutable.ListBuffer[Node[Any]] wenzelm@38479: var offset = parent.range.start wenzelm@38479: for ((_, (node, subtree)) <- branches.iterator) { wenzelm@38479: if (offset < node.range.start) wenzelm@38479: result += new Node(Text.Range(offset, node.range.start), parent.info) wenzelm@38479: result ++= subtree.flatten(node) wenzelm@38479: offset = node.range.stop wenzelm@38479: } wenzelm@38479: if (offset < parent.range.stop) wenzelm@38479: result += new Node(Text.Range(offset, parent.range.stop), parent.info) wenzelm@38479: result.toList wenzelm@38479: } wenzelm@38479: wenzelm@38564: def filter(pred: Node[Any] => Boolean): Markup_Tree = wenzelm@38479: { wenzelm@38479: val bs = branches.toList.flatMap(entry => { wenzelm@38479: val (_, (node, subtree)) = entry wenzelm@38479: if (pred(node)) List((node, (node, subtree.filter(pred)))) wenzelm@38479: else subtree.filter(pred).branches.toList wenzelm@38479: }) wenzelm@38479: new Markup_Tree(Branches.empty ++ bs) wenzelm@38479: } wenzelm@38479: wenzelm@38564: def select[A](range: Text.Range)(sel: PartialFunction[Any, A]): Stream[Node[List[A]]] = wenzelm@38486: { wenzelm@38564: def stream(parent: Node[List[A]], bs: Branches.T): Stream[Node[List[A]]] = wenzelm@38486: { wenzelm@38486: val substream = wenzelm@38562: (for ((_, (node, subtree)) <- Branches.overlapping(parent.range, bs).toStream) yield { wenzelm@38564: if (sel.isDefinedAt(node.info)) { wenzelm@38564: val current = Node(node.range.restrict(parent.range), List(sel(node.info))) wenzelm@38564: stream(current, subtree.branches) wenzelm@38564: } wenzelm@38486: else stream(parent, subtree.branches) wenzelm@38486: }).flatten wenzelm@38486: wenzelm@38564: def padding(last: Text.Offset, s: Stream[Node[List[A]]]): Stream[Node[List[A]]] = wenzelm@38486: s match { wenzelm@38562: case (node @ Node(Text.Range(start, stop), _)) #:: rest => wenzelm@38562: if (last < start) wenzelm@38562: parent.restrict(Text.Range(last, start)) #:: node #:: padding(stop, rest) wenzelm@38562: else node #:: padding(stop, rest) wenzelm@38564: case Stream.Empty => wenzelm@38486: if (last < parent.range.stop) wenzelm@38562: Stream(parent.restrict(Text.Range(last, parent.range.stop))) wenzelm@38486: else Stream.Empty wenzelm@38486: } wenzelm@38566: if (substream.isEmpty) Stream(parent) wenzelm@38566: else padding(parent.range.start, substream) wenzelm@38486: } wenzelm@38486: stream(Node(range, Nil), branches) wenzelm@38486: } wenzelm@38486: wenzelm@38564: def swing_tree(parent: DefaultMutableTreeNode)(swing_node: Node[Any] => DefaultMutableTreeNode) wenzelm@38479: { wenzelm@38479: for ((_, (node, subtree)) <- branches) { wenzelm@38479: val current = swing_node(node) wenzelm@38479: subtree.swing_tree(current)(swing_node) wenzelm@38479: parent.add(current) immler@34514: } immler@34514: } wenzelm@34717: } immler@34514: