summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
blob: 5c16bb44654567358f2242f6c3a4f2797819dd1a (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package scala.tools.nsc
package interactive

import scala.concurrent.SyncVar
import scala.util.control.ControlThrowable
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler}
import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._

/** Interface of interactive compiler to a client such as an IDE
 */
trait CompilerControl { self: Global =>

  /** Response {
    override def toString = "TypeMember("+sym+","+tpe+","+accessible+","+inherited+","+viaView+")"
  }{
    override def toString = "TypeMember("+sym+","+tpe+","+accessible+","+inherited+","+viaView+")"
  }wrapper to client
   */
  type Response[T] = SyncVar[Either[T, Throwable]]

  abstract class WorkItem extends (() => Unit)

  /** Info given for every member found by completion
   */
  abstract class Member {
    val sym: Symbol
    val tpe: Type
    val accessible: Boolean
  }

  case class TypeMember(sym: Symbol, tpe: Type, accessible: Boolean, inherited: Boolean, viaView: Symbol) extends Member
  case class ScopeMember(sym: Symbol, tpe: Type, accessible: Boolean, viaImport: Tree) extends Member

  /** The scheduler by which client and compiler communicate
   *  Must be initialized before starting compilerRunner
   */
  protected val scheduler = new WorkScheduler

  /** The compilation unit corresponding to a source file
   */
  def unitOf(s: SourceFile): RichCompilationUnit = unitOfFile get s.file match {
    case Some(unit) =>
      unit
    case None =>
      val unit = new RichCompilationUnit(s)
      unitOfFile(s.file) = unit
      unit
  }

  /** The compilation unit corresponding to a position */
  def unitOf(pos: Position): RichCompilationUnit = unitOf(pos.source)

  /** Remove the CompilationUnit corresponding to the given SourceFile
   *  from consideration for recompilation.
   */
  def removeUnitOf(s: SourceFile) = unitOfFile remove s.file

  /** Locate smallest tree that encloses position
   */
  def locateTree(pos: Position): Tree =
    new Locator(pos) locateIn unitOf(pos).body

  /** Locates smallest context that encloses position as an optional value.
   */
  def locateContext(pos: Position): Option[Context] =
    locateContext(unitOf(pos).contexts, pos)

  /** Returns the smallest context that contains given `pos`, throws FatalError if none exists.
   */
  def doLocateContext(pos: Position): Context = locateContext(pos) getOrElse {
    throw new FatalError("no context found for "+pos)
  }

  /** Make sure a set of compilation units is loaded and parsed.
   *  Return () to syncvar `result` on completion.
   */
  def askReload(sources: List[SourceFile], result: Response[Unit]) =
    scheduler postWorkItem new WorkItem {
      def apply() = reload(sources, result)
      override def toString = "reload "+sources
    }

  /** Set sync var `result` to a fully attributed tree located at position `pos`
   */
  def askTypeAt(pos: Position, result: Response[Tree]) =
    scheduler postWorkItem new WorkItem {
      def apply() = self.getTypedTreeAt(pos, result)
      override def toString = "typeat "+pos.source+" "+pos.show
    }

  def askType(source: SourceFile, forceReload: Boolean, result: Response[Tree]) =
    scheduler postWorkItem new WorkItem {
      def apply() = self.getTypedTree(source, forceReload, result)
      override def toString = "typecheck"
  }

  /** Set sync var `result' to list of members that are visible
   *  as members of the tree enclosing `pos`, possibly reachable by an implicit.
   *   - if `selection` is false, as identifiers in the scope enclosing `pos`
   */
  def askTypeCompletion(pos: Position, result: Response[List[Member]]) =
    scheduler postWorkItem new WorkItem {
      def apply() = self.getTypeCompletion(pos, result)
      override def toString = "type completion "+pos.source+" "+pos.show
    }

  /** Set sync var `result' to list of members that are visible
   *  as members of the scope enclosing `pos`.
   */
  def askScopeCompletion(pos: Position, result: Response[List[Member]]) =
    scheduler postWorkItem new WorkItem {
      def apply() = self.getScopeCompletion(pos, result)
      override def toString = "scope completion "+pos.source+" "+pos.show
    }

  /** Ask to do unit first on present and subsequent type checking passes */
  def askToDoFirst(f: SourceFile) = {
    scheduler postWorkItem new WorkItem {
      def apply() = moveToFront(List(f))
      override def toString = "dofirst "+f
    }
  }

  /** Cancel currently pending high-priority jobs */
  def askCancel() = scheduler raise CancelActionReq

  /** Cancel current compiler run and start a fresh one where everything will be re-typechecked
   *  (but not re-loaded).
   */
  def askReset() = scheduler raise FreshRunReq

  /** Tell the compile server to shutdown, and do not restart again */
  def askShutdown() = scheduler raise ShutdownReq

  // ---------------- Interpreted exeptions -------------------

  object CancelActionReq extends ControlThrowable
  object FreshRunReq extends ControlThrowable
  object ShutdownReq extends ControlThrowable
}