summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
blob: b3a290964e0113f6753ee3a13cb39c8483307407 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package scala.tools.nsc.interactive

import collection.mutable.ArrayBuffer
import nsc.util.Position

trait ContextTrees { self: Global =>

  type Context = analyzer.Context
  type Contexts = ArrayBuffer[ContextTree]

  class ContextTree(val context: Context, val children: ArrayBuffer[ContextTree]) {
    def this(context: Context) = this(context, new ArrayBuffer[ContextTree])
    def pos: Position = context.tree.pos
    override def toString = "ContextTree("+pos+", "+children+")"
  }

  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] = {
          assert(lo <= hi)
          val mid = (lo + hi) / 2
          val midpos = contexts(mid).pos
          if (pos precedes midpos)
            loop(lo, mid - 1)
          else if (midpos precedes pos)
            loop(mid + 1, hi)
          else if (midpos includes pos)
            locateContext(contexts(mid).children, pos) orElse Some(contexts(mid).context)
          else
            None
        }
        loop(0, hi)
      }
    }
  }

  def addContext(contexts: Contexts, context: Context) {
    val cpos = context.tree.pos
    try {
    if (!cpos.isDefined || cpos.isSynthetic) {}
    else if (contexts.isEmpty) contexts += new ContextTree(context)
    else {
      val hi = contexts.length - 1
      if (contexts(hi).pos properlyPrecedes cpos)
        contexts += new ContextTree(context)
      else if (contexts(hi).pos properlyIncludes cpos) // fast path w/o search
        addContext(contexts(hi).children, context)
      else if (cpos properlyPrecedes contexts(0).pos)
        new ContextTree(context) +: contexts
      else {
        def insertAt(idx: Int): Boolean = {
          val oldpos = contexts(idx).pos
          if (oldpos sameRange cpos) {
            contexts(idx) = new ContextTree(context, contexts(idx).children)
            true
          } else if (oldpos includes cpos) {
            addContext(contexts(idx).children, context)
            true
          } else if (cpos includes oldpos) {
            val start = contexts.indexWhere(cpos includes _.pos)
            val last = contexts.lastIndexWhere(cpos includes _.pos)
            contexts(start) = new ContextTree(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(context))
            else
              inform("internal error? skewed positions: "+lopos+" !< "+cpos+" !< "+hipos)
          }
        }
        loop(0, hi)
      }
    }
  } catch {
    case ex: Throwable =>
      println("failure inserting "+context.tree.pos+" into "+contexts+"/"+contexts(contexts.length - 1).pos+"/"+
              (contexts(contexts.length - 1).pos includes cpos))
      throw ex
  }}
}