@@ -53,61 +53,85 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
5353 def openedTrees : Map [URI , List [SourceTree ]] = myOpenedTrees
5454 def compilationUnits : Map [URI , CompilationUnit ] = myCompilationUnits
5555
56+ /**
57+ * The trees for all the source files in this project.
58+ *
59+ * This includes the trees for the buffers that are presently open in the IDE, and the trees
60+ * from the target directory.
61+ */
62+ def sourceTrees (implicit ctx : Context ): List [SourceTree ] = sourceTreesContaining(" " )
63+
64+ /**
65+ * The trees for all the source files in this project that contain `id`.
66+ *
67+ * This includes the trees for the buffers that are presently open in the IDE, and the trees
68+ * from the target directory.
69+ */
70+ def sourceTreesContaining (id : String )(implicit ctx : Context ): List [SourceTree ] = {
71+ val fromBuffers = openedTrees.values.flatten.toList
72+ val fromCompilationOutput = {
73+ val classNames = new mutable.ListBuffer [String ]
74+ val output = ctx.settings.outputDir.value
75+ if (output.isDirectory) {
76+ classesFromDir(output.jpath, classNames)
77+ } else {
78+ val zipFile = new ZipFile (output.file)
79+ classesFromZip(zipFile, classNames)
80+ }
81+ classNames.flatMap { cls =>
82+ val className = cls.toTypeName
83+ treesFromClassName(className, id = " " )
84+ }
85+ }
86+ (fromBuffers ++ fromCompilationOutput).distinct
87+ }
88+
89+ /**
90+ * All the trees for this project.
91+ *
92+ * This includes the trees of the sources of this project, along with the trees that are found
93+ * on this project's classpath.
94+ */
5695 def allTrees (implicit ctx : Context ): List [SourceTree ] = allTreesContaining(" " )
5796
97+ /**
98+ * All the trees for this project that contain `id`.
99+ *
100+ * This includes the trees of the sources of this project, along with the trees that are found
101+ * on this project's classpath.
102+ */
58103 def allTreesContaining (id : String )(implicit ctx : Context ): List [SourceTree ] = {
59104 val fromSource = openedTrees.values.flatten.toList
60105 val fromClassPath = (dirClassPathClasses ++ zipClassPathClasses).flatMap { cls =>
61106 val className = cls.toTypeName
62- List (tree( className, id), tree(className.moduleClassName, id)).flatten
107+ treesFromClassName( className, id)
63108 }
64109 (fromSource ++ fromClassPath).distinct
65110 }
66111
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
112+ /**
113+ * The `SourceTree`s that define the class `className` and/or module `className`.
114+ *
115+ * @see SourceTree.fromSymbol
116+ */
117+ private def treesFromClassName (className : TypeName , id : String )(implicit ctx : Context ): List [SourceTree ] = {
118+ def tree (className : TypeName , id : String ): Option [SourceTree ] = {
119+ val clsd = ctx.base.staticRef(className)
120+ clsd match {
121+ case clsd : ClassDenotation =>
122+ clsd.ensureCompleted()
123+ SourceTree .fromSymbol(clsd.symbol.asClass, id)
124+ case _ =>
125+ None
126+ }
75127 }
128+ List (tree(className, id), tree(className.moduleClassName, id)).flatten
76129 }
77130
78131 // Presence of a file with one of these suffixes indicates that the
79132 // corresponding class has been pickled with TASTY.
80133 private val tastySuffixes = List (" .hasTasty" , " .tasty" )
81134
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-
111135 // FIXME: All the code doing classpath handling is very fragile and ugly,
112136 // improving this requires changing the dotty classpath APIs to handle our usecases.
113137 // We also need something like sbt server-mode to be informed of changes on
@@ -128,17 +152,13 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
128152 }
129153
130154 // 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)
133-
134- 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)
155+ private val zipClassPathClasses : Seq [String ] = {
156+ val names = new mutable.ListBuffer [String ]
157+ zipClassPaths.foreach { zipCp =>
158+ val zipFile = new ZipFile (zipCp.zipFile)
159+ classesFromZip(zipFile, names)
140160 }
141- finally zipFile.close()
161+ names
142162 }
143163
144164 // FIXME: classfiles in directories may change at any point, so we retraverse
@@ -148,26 +168,43 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
148168 val names = new mutable.ListBuffer [String ]
149169 dirClassPaths.foreach { dirCp =>
150170 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- }
171+ classesFromDir(root, names)
172+ }
173+ names.toList
174+ }
175+
176+ /** Adds the names of the classes that are defined in `zipFile` to `buffer`. */
177+ private def classesFromZip (zipFile : ZipFile , buffer : mutable.ListBuffer [String ]): Unit = {
178+ try {
179+ for {
180+ entry <- zipFile.stream.toArray((size : Int ) => new Array [ZipEntry ](size))
181+ name = entry.getName
182+ tastySuffix <- tastySuffixes.find(name.endsWith)
183+ } buffer += name.replace(" /" , " ." ).stripSuffix(tastySuffix)
184+ }
185+ finally zipFile.close()
186+ }
187+
188+ /** Adds the names of the classes that are defined in `dir` to `buffer`. */
189+ private def classesFromDir (dir : Path , buffer : mutable.ListBuffer [String ]): Unit = {
190+ try
191+ Files .walkFileTree(dir, new SimpleFileVisitor [Path ] {
192+ override def visitFile (path : Path , attrs : BasicFileAttributes ) = {
193+ if (! attrs.isDirectory) {
194+ val name = path.getFileName.toString
195+ for {
196+ tastySuffix <- tastySuffixes
197+ if name.endsWith(tastySuffix)
198+ } {
199+ buffer += dir.relativize(path).toString.replace(" /" , " ." ).stripSuffix(tastySuffix)
162200 }
163- FileVisitResult .CONTINUE
164201 }
165- })
166- catch {
167- case _ : NoSuchFileException =>
168- }
202+ FileVisitResult .CONTINUE
203+ }
204+ })
205+ catch {
206+ case _ : NoSuchFileException =>
169207 }
170- names.toList
171208 }
172209
173210 private def topLevelClassTrees (topTree : Tree , source : SourceFile ): List [SourceTree ] = {
0 commit comments