summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2009-05-29 18:35:04 +0000
committerMartin Odersky <odersky@gmail.com>2009-05-29 18:35:04 +0000
commit488f986078c4f80381849f66d30d2e24818dbff9 (patch)
tree240ba26b107e11a94ff79a5a223ded056724ac77
parentda10214991cba1fa963453c66f3af94ac5ac6db4 (diff)
downloadscala-488f986078c4f80381849f66d30d2e24818dbff9.tar.gz
scala-488f986078c4f80381849f66d30d2e24818dbff9.tar.bz2
scala-488f986078c4f80381849f66d30d2e24818dbff9.zip
first attempt at command completion; more fiddl...
first attempt at command completion; more fiddling with positions
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala5
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala17
-rwxr-xr-xsrc/compiler/scala/tools/nsc/interactive/Global.scala74
-rwxr-xr-xsrc/compiler/scala/tools/nsc/interactive/Positions.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interactive/REPL.scala38
5 files changed, 106 insertions, 46 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 3de4df932f..97852afe81 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -428,7 +428,8 @@ trait Trees {
object emptyValDef
extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) {
override def isEmpty = true
- setPos(NoPosition)
+ super.setPos(NoPosition)
+ override def setPos(pos: Position) = { assert(false); this }
}
/** Method definition
@@ -612,7 +613,7 @@ trait Trees {
!vparamss1.head.isEmpty && (vparamss1.head.head.mods.flags & IMPLICIT) != 0)
vparamss1 = List() :: vparamss1;
val superRef: Tree = Select(Super(nme.EMPTY.toTypeName, nme.EMPTY.toTypeName), nme.CONSTRUCTOR)
- val superCall = atPos(parents.head) { (superRef /: argss) (Apply) }
+ val superCall = (superRef /: argss) (Apply)
List(
DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(()))))
}
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
index 8d4c18fbf7..9db09c7f88 100644
--- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
+++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
@@ -10,6 +10,14 @@ import scala.tools.nsc.ast._
*/
trait CompilerControl { self: Global =>
+ object MemberStatus extends Enumeration {
+ val Accessible, Inherited, Implicit = Value
+ }
+
+ type Response[T] = SyncVar[Either[T, Throwable]]
+
+ type Member = (Symbol, Type, MemberStatus.ValueSet)
+
/* Must be initialized before starting compilerRunner */
protected val scheduler = new WorkScheduler
@@ -38,12 +46,15 @@ trait CompilerControl { self: Global =>
locateContext(unitOf(pos).contexts, pos)
/** Make sure a set of compilation units is loaded and parsed */
- def askReload(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) =
+ def askReload(sources: List[SourceFile], result: Response[Unit]) =
scheduler.postWorkItem(() => reload(sources, result))
/** Set sync var `result` to a fully attributed tree located at position `pos` */
- def askTypeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) =
- scheduler.postWorkItem(() => self.typedTreeAt(pos, result))
+ def askTypeAt(pos: Position, result: Response[Tree]) =
+ scheduler.postWorkItem(() => self.getTypedTreeAt(pos, result))
+
+ def askCompletion(pos: Position, result: Response[List[Member]]) =
+ scheduler.postWorkItem(() => self.completion(pos, result))
/** Ask to do unit first on present and subsequent type checking passes */
def askToDoFirst(f: SourceFile) = {
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index 7d1333fab1..325718f865 100755
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -1,7 +1,6 @@
package scala.tools.nsc.interactive
import scala.collection.mutable.{LinkedHashMap, SynchronizedMap}
-import scala.concurrent.SyncVar
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, RangePosition, OffsetPosition, NoPosition, WorkScheduler}
import scala.tools.nsc.reporters._
@@ -191,44 +190,73 @@ self =>
// ----------------- Implementations of client commmands -----------------------
+ def respond[T](result: Response[T])(op: => T): Unit = try {
+ result set Left(op)
+ } catch {
+ case ex =>
+ result set Right(ex)
+ throw ex
+ }
+
/** Make sure a set of compilation units is loaded and parsed */
- def reload(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) {
- try {
+ def reload(sources: List[SourceFile], result: Response[Unit]) {
+ respond(result) {
currentTyperRun = new TyperRun()
for (source <- sources) {
val unit = new RichCompilationUnit(source)
unitOfFile(source.file) = unit
parse(unit)
+ if (settings.Xprintpos.value) treePrinter.print(unit)
}
moveToFront(sources)
- result set Left(())
- } catch {
- case ex =>
- result set Right(ex)
- throw ex
+ ()
}
if (outOfDate) throw new FreshRunReq
else outOfDate = true
}
+ /** A fully attributed tree located at position `pos` */
+ def typedTreeAt(pos: Position): Tree = {
+ val unit = unitOf(pos)
+ assert(unit.status != NotLoaded)
+ moveToFront(List(unit.source))
+ val typedTree = currentTyperRun.typedTreeAt(pos)
+ new Locator(pos) locateIn typedTree
+ }
+
/** Set sync var `result` to a fully attributed tree located at position `pos` */
- def typedTreeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) {
- try {
- println("typed tree at "+pos.show)
- val unit = unitOf(pos)
- assert(unit.status != NotLoaded)
- moveToFront(List(unit.source))
- val typedTree = currentTyperRun.typedTreeAt(pos)
- val located = new Locator(pos) locateIn typedTree
- result set Left(located)
- } catch {
- case ex =>
- result set Right(ex)
- throw ex
- }
+ def getTypedTreeAt(pos: Position, result: Response[Tree]) {
+ respond(result)(typedTreeAt(pos))
}
- def typeCompletion() {}
+ def stabilizedType(tree: Tree): Type = tree match {
+ case Ident(_) if tree.symbol.isStable => singleType(NoPrefix, tree.symbol)
+ case Select(qual, _) if tree.symbol.isStable => singleType(qual.tpe, tree.symbol)
+ case _ => tree.tpe
+ }
+
+ def completion(pos: Position, result: Response[List[Member]]) {
+ import MemberStatus._
+ respond(result) {
+ val tree = typedTreeAt(pos)
+ locateContext(pos) match {
+ case Some(context) =>
+ val superAccess = tree.isInstanceOf[Super]
+ val pre = stabilizedType(tree)
+ def withStatus(sym: Symbol, vs: ValueSet) = (
+ sym,
+ pre.memberType(sym),
+ if (context.isAccessible(sym, pre, superAccess)) vs + Accessible else vs
+ )
+ val decls = tree.tpe.decls.toList map (sym => withStatus(sym, ValueSet()))
+ val inherited = tree.tpe.members.toList diff decls map (sym => withStatus(sym, ValueSet(Inherited)))
+ val implicits = List() // not yet done
+ decls ::: inherited ::: implicits
+ case None =>
+ throw new FatalError("no context found for "+pos)
+ }
+ }
+ }
// ---------------- Helper classes ---------------------------
diff --git a/src/compiler/scala/tools/nsc/interactive/Positions.scala b/src/compiler/scala/tools/nsc/interactive/Positions.scala
index 36c60e7a2c..141f710b88 100755
--- a/src/compiler/scala/tools/nsc/interactive/Positions.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Positions.scala
@@ -29,11 +29,17 @@ self: Global =>
}
class TransparentPosition(source0: SourceFile, start: Int, point: Int, end: Int) extends RangePosition(source0, start, point, end) {
- override def toString = "<"+super.toString+">"
+ override def show = "<"+super.show+">"
}
def isTransparent(pos: Position) = pos.isInstanceOf[TransparentPosition]
def isRange(pos: Position) = pos.isInstanceOf[RangePosition]
+ def isPositionable(tree: Tree) = tree match {
+ case EmptyTree => false
+ case `emptyValDef` => false
+ case TypeTree() => tree.tpe != NoType
+ case _ => true
+ }
// -------------- ensuring no overlaps -------------------------------
@@ -136,7 +142,7 @@ self: Global =>
private def setChildrenPos(pos: Position, trees: List[Tree]) {
var currentPos = pos
for (tree <- trees) {
- if (tree != EmptyTree && tree.pos == NoPosition) {
+ if (isPositionable(tree) && tree.pos == NoPosition) {
val children = tree.children
if (children.isEmpty)
tree setPos OffsetPosition(pos.source.get, currentPos.start)
@@ -157,7 +163,7 @@ self: Global =>
*/
override def atPos[T <: Tree](pos: Position)(tree: T): T =
if (isRange(pos)) {
- if (tree != EmptyTree && tree.pos == NoPosition) {
+ if (isPositionable(tree) && tree.pos == NoPosition) {
tree.setPos(pos)
val children = tree.children
if (children.nonEmpty) {
@@ -180,11 +186,11 @@ self: Global =>
inform(tree.toString)
}
def validate(tree: Tree, encltree: Tree) {
- if (tree != EmptyTree && !tree.pos.isDefined)
+ if (isPositionable(tree) && !tree.pos.isDefined)
error("tree without position: "+tree)
- if (encltree.pos.isSynthetic && !tree.pos.isSynthetic)
+ if (encltree.pos.isSynthetic && !tree.pos.isDefined && tree.pos.isSynthetic)
error("synthetic "+encltree+" contains nonsynthetic" + tree)
- if (!(encltree.pos includes tree.pos))
+ if (tree.pos.isDefined && !(encltree.pos includes tree.pos))
error(encltree+" does not include "+tree)
for ((t1, t2) <- findOverlapping(tree.children flatMap solidDescendants))
error("overlapping trees: "+t1+" === and === "+t2)
diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala
index 331cdd597a..86e680a599 100644
--- a/src/compiler/scala/tools/nsc/interactive/REPL.scala
+++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala
@@ -75,25 +75,39 @@ object REPL {
/** Commands:
*
* reload file1 ... fileN
- * typeat file line col
- *
- *
+ * typeat file line off1 off2?
+ * complete file line off1 off2?
*/
def run(comp: Global) {
- val reloadResult = new SyncVar[Either[Unit, Throwable]]
- val typeatResult = new SyncVar[Either[comp.Tree, Throwable]]
+ val reloadResult = new comp.Response[Unit]
+ val typeatResult = new comp.Response[comp.Tree]
+ val completeResult = new comp.Response[List[comp.Member]]
+ def makePos(file: String, off1: String, off2: String) = {
+ val source = toSourceFile(file)
+ comp.rangePos(source, off1.toInt, off1.toInt, off2.toInt)
+ }
+ def doTypeAt(pos: Position) {
+ comp.askTypeAt(pos, typeatResult)
+ show(typeatResult)
+ }
+ def doComplete(pos: Position) {
+ comp.askCompletion(pos, completeResult)
+ show(completeResult)
+ }
loop { line =>
(line split " ").toList match {
case "reload" :: args =>
comp.askReload(args map toSourceFile, reloadResult)
show(reloadResult)
- case "typeat" :: file :: line :: col1 :: col2 :: Nil =>
- val source = toSourceFile(file)
- val linestart = source.lineToOffset(line.toInt)
- val pos = comp.rangePos(source, linestart + col1.toInt, linestart + col1.toInt, linestart + col2.toInt)
- comp.askTypeAt(pos, typeatResult)
- show(typeatResult)
- case "quit" :: Nil =>
+ case List("typeat", file, off1, off2) =>
+ doTypeAt(makePos(file, off1, off2))
+ case List("typeat", file, off1) =>
+ doTypeAt(makePos(file, off1, off1))
+ case List("complete", file, off1, off2) =>
+ doComplete(makePos(file, off1, off2))
+ case List("complete", file, off1) =>
+ doComplete(makePos(file, off1, off1))
+ case List("quit") =>
System.exit(1)
case _ =>
println("unrecongized command")