@@ -36,78 +36,27 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
3636 }
3737
3838 private [this ] var myCtx : Context = myInitCtx
39-
4039 def currentCtx : Context = myCtx
4140
41+ private val compiler : Compiler = new InteractiveCompiler
42+
4243 private val myOpenedFiles = new mutable.LinkedHashMap [URI , SourceFile ] {
4344 override def default (key : URI ) = NoSource
4445 }
46+ def openedFiles : Map [URI , SourceFile ] = myOpenedFiles
4547
4648 private val myOpenedTrees = new mutable.LinkedHashMap [URI , List [SourceTree ]] {
4749 override def default (key : URI ) = Nil
4850 }
51+ def openedTrees : Map [URI , List [SourceTree ]] = myOpenedTrees
4952
5053 private val myCompilationUnits = new mutable.LinkedHashMap [URI , CompilationUnit ]
51-
52- def openedFiles : Map [URI , SourceFile ] = myOpenedFiles
53- def openedTrees : Map [URI , List [SourceTree ]] = myOpenedTrees
5454 def compilationUnits : Map [URI , CompilationUnit ] = myCompilationUnits
5555
56- def allTrees (implicit ctx : Context ): List [SourceTree ] = allTreesContaining(" " )
57-
58- def allTreesContaining (id : String )(implicit ctx : Context ): List [SourceTree ] = {
59- val fromSource = openedTrees.values.flatten.toList
60- val fromClassPath = (dirClassPathClasses ++ zipClassPathClasses).flatMap { cls =>
61- val className = cls.toTypeName
62- List (tree(className, id), tree(className.moduleClassName, id)).flatten
63- }
64- (fromSource ++ fromClassPath).distinct
65- }
66-
67- private def tree (className : TypeName , id : String )(implicit ctx : Context ): Option [SourceTree ] = {
68- val clsd = ctx.base.staticRef(className)
69- clsd match {
70- case clsd : ClassDenotation =>
71- clsd.ensureCompleted()
72- SourceTree .fromSymbol(clsd.symbol.asClass, id)
73- case _ =>
74- None
75- }
76- }
77-
7856 // Presence of a file with one of these suffixes indicates that the
7957 // corresponding class has been pickled with TASTY.
8058 private val tastySuffixes = List (" .hasTasty" , " .tasty" )
8159
82- private def classNames (cp : ClassPath , packageName : String ): List [String ] = {
83- def className (classSegments : List [String ]) =
84- classSegments.mkString(" ." ).stripSuffix(" .class" )
85-
86- val ClassPathEntries (pkgs, classReps) = cp.list(packageName)
87-
88- classReps
89- .filter((classRep : ClassRepresentation ) => classRep.binary match {
90- case None =>
91- true
92- case Some (binFile) =>
93- val prefix =
94- if (binFile.name.endsWith(" .class" ))
95- binFile.name.stripSuffix(" .class" )
96- else
97- null
98- prefix != null && {
99- binFile match {
100- case pf : PlainFile =>
101- tastySuffixes.map(suffix => pf.givenPath.parent / (prefix + suffix)).exists(_.exists)
102- case _ =>
103- sys.error(s " Unhandled file type: $binFile [getClass = ${binFile.getClass}] " )
104- }
105- }
106- })
107- .map(classRep => (packageName ++ (if (packageName != " " ) " ." else " " ) ++ classRep.name)).toList ++
108- pkgs.flatMap(pkg => classNames(cp, pkg.name))
109- }
110-
11160 // FIXME: All the code doing classpath handling is very fragile and ugly,
11261 // improving this requires changing the dotty classpath APIs to handle our usecases.
11362 // We also need something like sbt server-mode to be informed of changes on
@@ -128,46 +77,173 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
12877 }
12978
13079 // Like in `ZipArchiveFileLookup` we assume that zips are immutable
131- private val zipClassPathClasses : Seq [String ] = zipClassPaths.flatMap { zipCp =>
132- val zipFile = new ZipFile (zipCp.zipFile)
80+ private val zipClassPathClasses : Seq [TypeName ] = {
81+ val names = new mutable.ListBuffer [TypeName ]
82+ zipClassPaths.foreach { zipCp =>
83+ val zipFile = new ZipFile (zipCp.zipFile)
84+ classesFromZip(zipFile, names)
85+ }
86+ names
87+ }
88+
89+ initialize()
90+
91+ /**
92+ * The trees for all the source files in this project.
93+ *
94+ * This includes the trees for the buffers that are presently open in the IDE, and the trees
95+ * from the target directory.
96+ */
97+ def sourceTrees (implicit ctx : Context ): List [SourceTree ] = sourceTreesContaining(" " )
98+
99+ /**
100+ * The trees for all the source files in this project that contain `id`.
101+ *
102+ * This includes the trees for the buffers that are presently open in the IDE, and the trees
103+ * from the target directory.
104+ */
105+ def sourceTreesContaining (id : String )(implicit ctx : Context ): List [SourceTree ] = {
106+ val fromBuffers = openedTrees.values.flatten.toList
107+ val fromCompilationOutput = {
108+ val classNames = new mutable.ListBuffer [TypeName ]
109+ val output = ctx.settings.outputDir.value
110+ if (output.isDirectory) {
111+ classesFromDir(output.jpath, classNames)
112+ } else {
113+ val zipFile = new ZipFile (output.file)
114+ classesFromZip(zipFile, classNames)
115+ }
116+ classNames.flatMap { cls =>
117+ treesFromClassName(cls, id)
118+ }
119+ }
120+ (fromBuffers ++ fromCompilationOutput).distinct
121+ }
133122
123+ /**
124+ * All the trees for this project.
125+ *
126+ * This includes the trees of the sources of this project, along with the trees that are found
127+ * on this project's classpath.
128+ */
129+ def allTrees (implicit ctx : Context ): List [SourceTree ] = allTreesContaining(" " )
130+
131+ /**
132+ * All the trees for this project that contain `id`.
133+ *
134+ * This includes the trees of the sources of this project, along with the trees that are found
135+ * on this project's classpath.
136+ */
137+ def allTreesContaining (id : String )(implicit ctx : Context ): List [SourceTree ] = {
138+ val fromSource = openedTrees.values.flatten.toList
139+ val fromClassPath = (dirClassPathClasses ++ zipClassPathClasses).flatMap { cls =>
140+ treesFromClassName(cls, id)
141+ }
142+ (fromSource ++ fromClassPath).distinct
143+ }
144+
145+ def run (uri : URI , sourceCode : String ): List [MessageContainer ] = run(uri, toSource(uri, sourceCode))
146+
147+ def run (uri : URI , source : SourceFile ): List [MessageContainer ] = {
148+ val previousCtx = myCtx
134149 try {
135- for {
136- entry <- zipFile.stream.toArray((size : Int ) => new Array [ZipEntry ](size))
137- name = entry.getName
138- tastySuffix <- tastySuffixes.find(name.endsWith)
139- } yield name.replace(" /" , " ." ).stripSuffix(tastySuffix)
150+ val reporter =
151+ new StoreReporter (null ) with UniqueMessagePositions with HideNonSensicalMessages
152+
153+ val run = compiler.newRun(myInitCtx.fresh.setReporter(reporter))
154+ myCtx = run.runContext
155+
156+ implicit val ctx = myCtx
157+
158+ myOpenedFiles(uri) = source
159+
160+ run.compileSources(List (source))
161+ run.printSummary()
162+ val unit = ctx.run.units.head
163+ val t = unit.tpdTree
164+ cleanup(t)
165+ myOpenedTrees(uri) = topLevelClassTrees(t, source)
166+ myCompilationUnits(uri) = unit
167+
168+ reporter.removeBufferedMessages
169+ }
170+ catch {
171+ case ex : FatalError =>
172+ myCtx = previousCtx
173+ close(uri)
174+ Nil
175+ }
176+ }
177+
178+ def close (uri : URI ): Unit = {
179+ myOpenedFiles.remove(uri)
180+ myOpenedTrees.remove(uri)
181+ myCompilationUnits.remove(uri)
182+ }
183+
184+ /**
185+ * The `SourceTree`s that define the class `className` and/or module `className`.
186+ *
187+ * @see SourceTree.fromSymbol
188+ */
189+ private def treesFromClassName (className : TypeName , id : String )(implicit ctx : Context ): List [SourceTree ] = {
190+ def tree (className : TypeName , id : String ): Option [SourceTree ] = {
191+ val clsd = ctx.base.staticRef(className)
192+ clsd match {
193+ case clsd : ClassDenotation =>
194+ clsd.ensureCompleted()
195+ SourceTree .fromSymbol(clsd.symbol.asClass, id)
196+ case _ =>
197+ None
198+ }
140199 }
141- finally zipFile.close()
200+ List (tree(className, id), tree(className.moduleClassName, id)).flatten
142201 }
143202
144203 // FIXME: classfiles in directories may change at any point, so we retraverse
145204 // the directories each time, if we knew when classfiles changed (sbt
146205 // server-mode might help here), we could do cache invalidation instead.
147- private def dirClassPathClasses : Seq [String ] = {
148- val names = new mutable.ListBuffer [String ]
206+ private def dirClassPathClasses : Seq [TypeName ] = {
207+ val names = new mutable.ListBuffer [TypeName ]
149208 dirClassPaths.foreach { dirCp =>
150209 val root = dirCp.dir.toPath
151- try
152- Files .walkFileTree(root, new SimpleFileVisitor [Path ] {
153- override def visitFile (path : Path , attrs : BasicFileAttributes ) = {
154- if (! attrs.isDirectory) {
155- val name = path.getFileName.toString
156- for {
157- tastySuffix <- tastySuffixes
158- if name.endsWith(tastySuffix)
159- } {
160- names += root.relativize(path).toString.replace(" /" , " ." ).stripSuffix(tastySuffix)
161- }
210+ classesFromDir(root, names)
211+ }
212+ names
213+ }
214+
215+ /** Adds the names of the classes that are defined in `zipFile` to `buffer`. */
216+ private def classesFromZip (zipFile : ZipFile , buffer : mutable.ListBuffer [TypeName ]): Unit = {
217+ try {
218+ for {
219+ entry <- zipFile.stream.toArray((size : Int ) => new Array [ZipEntry ](size))
220+ name = entry.getName
221+ tastySuffix <- tastySuffixes.find(name.endsWith)
222+ } buffer += name.replace(" /" , " ." ).stripSuffix(tastySuffix).toTypeName
223+ }
224+ finally zipFile.close()
225+ }
226+
227+ /** Adds the names of the classes that are defined in `dir` to `buffer`. */
228+ private def classesFromDir (dir : Path , buffer : mutable.ListBuffer [TypeName ]): Unit = {
229+ try
230+ Files .walkFileTree(dir, new SimpleFileVisitor [Path ] {
231+ override def visitFile (path : Path , attrs : BasicFileAttributes ) = {
232+ if (! attrs.isDirectory) {
233+ val name = path.getFileName.toString
234+ for {
235+ tastySuffix <- tastySuffixes
236+ if name.endsWith(tastySuffix)
237+ } {
238+ buffer += dir.relativize(path).toString.replace(" /" , " ." ).stripSuffix(tastySuffix).toTypeName
162239 }
163- FileVisitResult .CONTINUE
164240 }
165- })
166- catch {
167- case _ : NoSuchFileException =>
168- }
241+ FileVisitResult .CONTINUE
242+ }
243+ })
244+ catch {
245+ case _ : NoSuchFileException =>
169246 }
170- names.toList
171247 }
172248
173249 private def topLevelClassTrees (topTree : Tree , source : SourceFile ): List [SourceTree ] = {
@@ -185,8 +261,6 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
185261 trees.toList
186262 }
187263
188- private val compiler : Compiler = new InteractiveCompiler
189-
190264 /** Remove attachments and error out completers. The goal is to avoid
191265 * having a completer hanging in a typed tree which can capture the context
192266 * of a previous run. Note that typed trees can have untyped or partially
@@ -224,44 +298,20 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
224298 new SourceFile (virtualFile, Codec .UTF8 )
225299 }
226300
227- def run (uri : URI , sourceCode : String ): List [MessageContainer ] = run(uri, toSource(uri, sourceCode))
228-
229- def run (uri : URI , source : SourceFile ): List [MessageContainer ] = {
230- val previousCtx = myCtx
231- try {
232- val reporter =
233- new StoreReporter (null ) with UniqueMessagePositions with HideNonSensicalMessages
234-
235- val run = compiler.newRun(myInitCtx.fresh.setReporter(reporter))
236- myCtx = run.runContext
237-
238- implicit val ctx = myCtx
239-
240- myOpenedFiles(uri) = source
241-
242- run.compileSources(List (source))
243- run.printSummary()
244- val unit = ctx.run.units.head
245- val t = unit.tpdTree
246- cleanup(t)
247- myOpenedTrees(uri) = topLevelClassTrees(t, source)
248- myCompilationUnits(uri) = unit
249-
250- reporter.removeBufferedMessages
251- }
252- catch {
253- case ex : FatalError =>
254- myCtx = previousCtx
255- close(uri)
256- Nil
257- }
301+ /**
302+ * Initialize this driver and compiler.
303+ *
304+ * This is necessary because an `InteractiveDriver` can be put to work without having
305+ * compiled anything (for instance, resolving a symbol coming from a different compiler in
306+ * this compiler). In those cases, an un-initialized compiler may crash (for instance if
307+ * late-compilation is needed).
308+ */
309+ private [this ] def initialize (): Unit = {
310+ val run = compiler.newRun(myInitCtx.fresh)
311+ myCtx = run.runContext
312+ run.compileUnits(Nil , myCtx)
258313 }
259314
260- def close (uri : URI ): Unit = {
261- myOpenedFiles.remove(uri)
262- myOpenedTrees.remove(uri)
263- myCompilationUnits.remove(uri)
264- }
265315}
266316
267317object InteractiveDriver {
0 commit comments