package scala.tools.nsc package interactive import collection.mutable.ArrayBuffer import util.Position trait ContextTrees { self: Global => type Context = analyzer.Context lazy val NoContext = analyzer.NoContext type Contexts = ArrayBuffer[ContextTree] /** A context tree contains contexts that are indexed by positions. * It satisfies the following properties: * 1. All context come from compiling the same unit. * 2. Child contexts have parent contexts in their outer chain. * 3. The `pos` field of a context is the same as `context.tree.pos`, unless that * position is transparent. In that case, `pos` equals the position of * one of the solid descendants of `context.tree`. * 4. Children of a context have non-overlapping increasining positions. * 5. No context in the tree has a transparent position. */ class ContextTree(val pos: Position, val context: Context, val children: ArrayBuffer[ContextTree]) { def this(pos: Position, context: Context) = this(pos, context, new ArrayBuffer[ContextTree]) override def toString = "ContextTree("+pos+", "+children+")" } /** Optionally returns the smallest context that contains given `pos`, or None if none exists. */ def locateContext(contexts: Contexts, pos: Position): Option[Context] = { if (contexts.isEmpty) None else { val hi = contexts.length - 1 if ((contexts(hi).pos precedes pos) || (pos precedes contexts(0).pos)) None else { def loop(lo: Int, hi: Int): Option[Context] = { val mid = (lo + hi) / 2 val midpos = contexts(mid).pos if ((pos precedes midpos) && (mid < hi)) loop(lo, mid) else if ((midpos precedes pos) && (lo < mid)) loop(mid, hi) else if (midpos includes pos) Some(contexts(mid).context) else if (contexts(mid+1).pos includes pos) Some(contexts(mid+1).context) else None } loop(0, hi) } } } /** Insert a context at correct position into a buffer of context trees. * If the `context` has a transparent position, add it multiple times * at the positions of all its solid descendant trees. */ def addContext(contexts: Contexts, context: Context) { val cpos = context.tree.pos if (cpos.isTransparent) for (t <- context.tree.children flatMap solidDescendants) addContext(contexts, context, t.pos) else addContext(contexts, context, cpos) } /** Insert a context with non-transparent position `cpos` * at correct position into a buffer of context trees. */ def addContext(contexts: Contexts, context: Context, cpos: Position) { try { if (!cpos.isRange) {} else if (contexts.isEmpty) contexts += new ContextTree(cpos, context) else { val hi = contexts.length - 1 if (contexts(hi).pos properlyPrecedes cpos) contexts += new ContextTree(cpos, context) else if (contexts(hi).pos properlyIncludes cpos) // fast path w/o search addContext(contexts(hi).children, context, cpos) else if (cpos properlyPrecedes contexts(0).pos) new ContextTree(cpos, context) +: contexts else { def insertAt(idx: Int): Boolean = { val oldpos = contexts(idx).pos if (oldpos sameRange cpos) { contexts(idx) = new ContextTree(cpos, context, contexts(idx).children) true } else if (oldpos includes cpos) { addContext(contexts(idx).children, context, cpos) true } else if (cpos includes oldpos) { val start = contexts.indexWhere(cpos includes _.pos) val last = contexts.lastIndexWhere(cpos includes _.pos) contexts(start) = new ContextTree(cpos, context, contexts.slice(start, last + 1)) contexts.remove(start + 1, last - start) true } else false } def loop(lo: Int, hi: Int) { if (hi - lo > 1) { val mid = (lo + hi) / 2 val midpos = contexts(mid).pos if (cpos precedes midpos) loop(lo, mid) else if (midpos precedes cpos) loop(mid, hi) } else if (!insertAt(lo) && !insertAt(hi)) { val lopos = contexts(lo).pos val hipos = contexts(hi).pos if ((lopos precedes cpos) && (cpos precedes hipos)) contexts.insert(hi, new ContextTree(cpos, context)) else inform("internal error? skewed positions: "+lopos+" !< "+cpos+" !< "+hipos) } } loop(0, hi) } } } catch { case ex: Throwable => println(ex) ex.printStackTrace() println("failure inserting "+cpos+" into "+contexts+"/"+contexts(contexts.length - 1).pos+"/"+ (contexts(contexts.length - 1).pos includes cpos)) throw ex } } }