Skip to content

Commit 6cb50ac

Browse files
committed
Enable flat classpath by default
Implements VirtualDirectoryFlatClassPath, which is required for the presentation compiler created for the repl's tab-completion. Various minor cleanups in the flat classpath implementation.
1 parent ad48e59 commit 6cb50ac

File tree

10 files changed

+163
-120
lines changed

10 files changed

+163
-120
lines changed

src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import scala.tools.nsc.util.ClassRepresentation
1818
* @param aggregates classpath instances containing entries which this class processes
1919
*/
2020
case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatClassPath {
21-
2221
override def findClassFile(className: String): Option[AbstractFile] = {
2322
@tailrec
2423
def find(aggregates: Seq[FlatClassPath]): Option[AbstractFile] =
@@ -37,16 +36,19 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl
3736
@tailrec
3837
def findEntry[T <: ClassRepClassPathEntry](aggregates: Seq[FlatClassPath], getEntries: FlatClassPath => Seq[T]): Option[T] =
3938
if (aggregates.nonEmpty) {
40-
val entry = getEntries(aggregates.head)
41-
.find(_.name == simpleClassName)
39+
val entry = getEntries(aggregates.head).find(_.name == simpleClassName)
4240
if (entry.isDefined) entry
4341
else findEntry(aggregates.tail, getEntries)
4442
} else None
4543

4644
val classEntry = findEntry(aggregates, classesGetter(pkg))
4745
val sourceEntry = findEntry(aggregates, sourcesGetter(pkg))
4846

49-
mergeClassesAndSources(classEntry.toList, sourceEntry.toList).headOption
47+
(classEntry, sourceEntry) match {
48+
case (Some(c), Some(s)) => Some(ClassAndSourceFilesEntry(c.file, s.file))
49+
case (c @ Some(_), _) => c
50+
case (_, s) => s
51+
}
5052
}
5153

5254
override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs)

src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,105 +4,108 @@
44
package scala.tools.nsc.classpath
55

66
import java.io.File
7-
import java.io.FileFilter
87
import java.net.URL
98
import scala.reflect.io.AbstractFile
109
import scala.reflect.io.PlainFile
1110
import scala.tools.nsc.util.ClassRepresentation
1211
import FileUtils._
1312

1413
/**
15-
* A trait allowing to look for classpath entries of given type in directories.
16-
* It provides common logic for classes handling class and source files.
14+
* A trait allowing to look for classpath entries in directories. It provides common logic for
15+
* classes handling class and source files.
1716
* It makes use of the fact that in the case of nested directories it's easy to find a file
1817
* when we have a name of a package.
18+
* It abstracts over the file representation to work with both JFile and AbstractFile.
1919
*/
20-
trait DirectoryFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath {
21-
val dir: File
22-
assert(dir != null, "Directory file in DirectoryFileLookup cannot be null")
20+
trait DirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath {
21+
type F
22+
23+
val dir: F
24+
25+
protected def emptyFiles: Array[F] // avoids reifying ClassTag[F]
26+
protected def getSubDir(dirName: String): Option[F]
27+
protected def listChildren(dir: F, filter: Option[F => Boolean] = None): Array[F]
28+
protected def getName(f: F): String
29+
protected def toAbstractFile(f: F): AbstractFile
30+
protected def isPackage(f: F): Boolean
2331

24-
override def asURLs: Seq[URL] = Seq(dir.toURI.toURL)
25-
override def asClassPathStrings: Seq[String] = Seq(dir.getPath)
32+
protected def createFileEntry(file: AbstractFile): FileEntryType
33+
protected def isMatchingFile(f: F): Boolean
2634

27-
import FlatClassPath.RootPackage
28-
private def getDirectory(forPackage: String): Option[File] = {
29-
if (forPackage == RootPackage) {
35+
private def getDirectory(forPackage: String): Option[F] = {
36+
if (forPackage == FlatClassPath.RootPackage) {
3037
Some(dir)
3138
} else {
3239
val packageDirName = FileUtils.dirPath(forPackage)
33-
val packageDir = new File(dir, packageDirName)
34-
if (packageDir.exists && packageDir.isDirectory) {
35-
Some(packageDir)
36-
} else None
40+
getSubDir(packageDirName)
3741
}
3842
}
3943

40-
override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
44+
private[nsc] def packages(inPackage: String): Seq[PackageEntry] = {
4145
val dirForPackage = getDirectory(inPackage)
42-
val nestedDirs: Array[File] = dirForPackage match {
43-
case None => Array.empty
44-
case Some(directory) => directory.listFiles(DirectoryFileLookup.packageDirectoryFileFilter)
46+
val nestedDirs: Array[F] = dirForPackage match {
47+
case None => emptyFiles
48+
case Some(directory) => listChildren(directory, Some(isPackage))
4549
}
4650
val prefix = PackageNameUtils.packagePrefix(inPackage)
47-
val entries = nestedDirs map { file =>
48-
PackageEntryImpl(prefix + file.getName)
49-
}
50-
entries
51+
nestedDirs.map(f => PackageEntryImpl(prefix + getName(f)))
5152
}
5253

5354
protected def files(inPackage: String): Seq[FileEntryType] = {
5455
val dirForPackage = getDirectory(inPackage)
55-
val files: Array[File] = dirForPackage match {
56-
case None => Array.empty
57-
case Some(directory) => directory.listFiles(fileFilter)
58-
}
59-
val entries = files map { file =>
60-
val wrappedFile = new scala.reflect.io.File(file)
61-
createFileEntry(new PlainFile(wrappedFile))
56+
val files: Array[F] = dirForPackage match {
57+
case None => emptyFiles
58+
case Some(directory) => listChildren(directory, Some(isMatchingFile))
6259
}
63-
entries
60+
files.map(f => createFileEntry(toAbstractFile(f)))
6461
}
6562

66-
override private[nsc] def list(inPackage: String): FlatClassPathEntries = {
63+
private[nsc] def list(inPackage: String): FlatClassPathEntries = {
6764
val dirForPackage = getDirectory(inPackage)
68-
val files: Array[File] = dirForPackage match {
69-
case None => Array.empty
70-
case Some(directory) => directory.listFiles()
65+
val files: Array[F] = dirForPackage match {
66+
case None => emptyFiles
67+
case Some(directory) => listChildren(directory)
7168
}
7269
val packagePrefix = PackageNameUtils.packagePrefix(inPackage)
7370
val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry]
7471
val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType]
7572
for (file <- files) {
76-
if (file.isPackage) {
77-
val pkgEntry = PackageEntryImpl(packagePrefix + file.getName)
78-
packageBuf += pkgEntry
79-
} else if (fileFilter.accept(file)) {
80-
val wrappedFile = new scala.reflect.io.File(file)
81-
val abstractFile = new PlainFile(wrappedFile)
82-
fileBuf += createFileEntry(abstractFile)
83-
}
73+
if (isPackage(file))
74+
packageBuf += PackageEntryImpl(packagePrefix + getName(file))
75+
else if (isMatchingFile(file))
76+
fileBuf += createFileEntry(toAbstractFile(file))
8477
}
8578
FlatClassPathEntries(packageBuf, fileBuf)
8679
}
87-
88-
protected def createFileEntry(file: AbstractFile): FileEntryType
89-
protected def fileFilter: FileFilter
9080
}
9181

92-
object DirectoryFileLookup {
82+
trait JFileDirectoryLookup[FileEntryType <: ClassRepClassPathEntry] extends DirectoryLookup[FileEntryType] {
83+
type F = File
9384

94-
private[classpath] object packageDirectoryFileFilter extends FileFilter {
95-
override def accept(pathname: File): Boolean = pathname.isPackage
85+
protected def emptyFiles: Array[File] = Array.empty
86+
protected def getSubDir(packageDirName: String): Option[File] = {
87+
val packageDir = new File(dir, packageDirName)
88+
if (packageDir.exists && packageDir.isDirectory) Some(packageDir)
89+
else None
9690
}
97-
}
91+
protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = filter match {
92+
case Some(f) => dir.listFiles(mkFileFilter(f))
93+
case None => dir.listFiles()
94+
}
95+
protected def getName(f: File): String = f.getName
96+
protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new scala.reflect.io.File(f))
97+
protected def isPackage(f: File): Boolean = f.isPackage
9898

99-
case class DirectoryFlatClassPath(dir: File)
100-
extends DirectoryFileLookup[ClassFileEntryImpl]
101-
with NoSourcePaths {
99+
assert(dir != null, "Directory file in DirectoryFileLookup cannot be null")
102100

101+
def asURLs: Seq[URL] = Seq(dir.toURI.toURL)
102+
def asClassPathStrings: Seq[String] = Seq(dir.getPath)
103+
}
104+
105+
case class DirectoryFlatClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
103106
override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl
104107

105-
override def findClassFile(className: String): Option[AbstractFile] = {
108+
def findClassFile(className: String): Option[AbstractFile] = {
106109
val relativePath = FileUtils.dirPath(className)
107110
val classFile = new File(s"$dir/$relativePath.class")
108111
if (classFile.exists) {
@@ -112,31 +115,19 @@ case class DirectoryFlatClassPath(dir: File)
112115
} else None
113116
}
114117

115-
override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
116-
override protected def fileFilter: FileFilter = DirectoryFlatClassPath.classFileFilter
117-
118-
override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
119-
}
120-
121-
object DirectoryFlatClassPath {
118+
protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
119+
protected def isMatchingFile(f: File): Boolean = f.isClass
122120

123-
private val classFileFilter = new FileFilter {
124-
override def accept(pathname: File): Boolean = pathname.isClass
125-
}
121+
private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
126122
}
127123

128-
case class DirectoryFlatSourcePath(dir: File)
129-
extends DirectoryFileLookup[SourceFileEntryImpl]
130-
with NoClassPaths {
124+
case class DirectoryFlatSourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths {
125+
def asSourcePathString: String = asClassPathString
131126

132-
override def asSourcePathString: String = asClassPathString
127+
protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file)
128+
protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName)
133129

134-
override protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file)
135-
override protected def fileFilter: FileFilter = DirectoryFlatSourcePath.sourceFileFilter
136-
137-
override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = {
138-
findSourceFile(className) map SourceFileEntryImpl
139-
}
130+
override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findSourceFile(className) map SourceFileEntryImpl
140131

141132
private def findSourceFile(className: String): Option[AbstractFile] = {
142133
val relativePath = FileUtils.dirPath(className)
@@ -151,12 +142,5 @@ case class DirectoryFlatSourcePath(dir: File)
151142
}
152143
}
153144

154-
override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
155-
}
156-
157-
object DirectoryFlatSourcePath {
158-
159-
private val sourceFileFilter = new FileFilter {
160-
override def accept(pathname: File): Boolean = endsScalaOrJava(pathname.getName)
161-
}
145+
private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage)
162146
}

src/compiler/scala/tools/nsc/classpath/FileUtils.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
package scala.tools.nsc.classpath
55

6-
import java.io.{ File => JFile }
6+
import java.io.{File => JFile, FileFilter}
77
import java.net.URL
88
import scala.reflect.internal.FatalError
99
import scala.reflect.io.AbstractFile
@@ -65,4 +65,8 @@ object FileUtils {
6565
// because then some tests in partest don't pass
6666
private def mayBeValidPackage(dirName: String): Boolean =
6767
(dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.')
68+
69+
def mkFileFilter(f: JFile => Boolean) = new FileFilter {
70+
def accept(pathname: JFile): Boolean = f(pathname)
71+
}
6872
}

src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@ trait FlatClassPath extends ClassFileLookup[AbstractFile] {
2828
override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = {
2929
val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className)
3030

31-
val foundClassFromClassFiles = classes(pkg)
32-
.find(_.name == simpleClassName)
33-
34-
def findClassInSources = sources(pkg)
35-
.find(_.name == simpleClassName)
31+
val foundClassFromClassFiles = classes(pkg).find(_.name == simpleClassName)
32+
def findClassInSources = sources(pkg).find(_.name == simpleClassName)
3633

3734
foundClassFromClassFiles orElse findClassInSources
3835
}
@@ -50,7 +47,7 @@ case class FlatClassPathEntries(packages: Seq[PackageEntry], classesAndSources:
5047
object FlatClassPathEntries {
5148
import scala.language.implicitConversions
5249
// to have working unzip method
53-
implicit def entry2Tuple(entry: FlatClassPathEntries) = (entry.packages, entry.classesAndSources)
50+
implicit def entry2Tuple(entry: FlatClassPathEntries): (Seq[PackageEntry], Seq[ClassRepClassPathEntry]) = (entry.packages, entry.classesAndSources)
5451
}
5552

5653
sealed trait ClassRepClassPathEntry extends ClassRepresentation[AbstractFile]

src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
package scala.tools.nsc.classpath
55

6+
import scala.reflect.io.VirtualDirectory
67
import scala.tools.nsc.Settings
78
import scala.tools.nsc.io.AbstractFile
89
import FileUtils.AbstractFileOps
@@ -12,16 +13,9 @@ import FileUtils.AbstractFileOps
1213
* it uses proper type of classpath depending on a types of particular files containing sources or classes.
1314
*/
1415
class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClassPath] {
16+
def newClassPath(file: AbstractFile): FlatClassPath = FlatClassPathFactory.newClassPath(file, settings)
1517

16-
override def newClassPath(file: AbstractFile): FlatClassPath =
17-
if (file.isJarOrZip)
18-
ZipAndJarFlatClassPathFactory.create(file, settings)
19-
else if (file.isDirectory)
20-
new DirectoryFlatClassPath(file.file)
21-
else
22-
sys.error(s"Unsupported classpath element: $file")
23-
24-
override def sourcesInPath(path: String): List[FlatClassPath] =
18+
def sourcesInPath(path: String): List[FlatClassPath] =
2519
for {
2620
file <- expandPath(path, expandStar = false)
2721
dir <- Option(AbstractFile getDirectory file)
@@ -35,3 +29,16 @@ class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClas
3529
else
3630
sys.error(s"Unsupported sourcepath element: $file")
3731
}
32+
33+
object FlatClassPathFactory {
34+
def newClassPath(file: AbstractFile, settings: Settings): FlatClassPath = file match {
35+
case vd: VirtualDirectory => VirtualDirectoryFlatClassPath(vd)
36+
case _ =>
37+
if (file.isJarOrZip)
38+
ZipAndJarFlatClassPathFactory.create(file, settings)
39+
else if (file.isDirectory)
40+
new DirectoryFlatClassPath(file.file)
41+
else
42+
sys.error(s"Unsupported classpath element: $file")
43+
}
44+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package scala.tools.nsc.classpath
2+
3+
import scala.tools.nsc.util.ClassRepresentation
4+
import scala.reflect.io.{Path, PlainFile, VirtualDirectory, AbstractFile}
5+
import FileUtils._
6+
import java.net.URL
7+
8+
case class VirtualDirectoryFlatClassPath(dir: VirtualDirectory) extends FlatClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
9+
type F = AbstractFile
10+
11+
protected def emptyFiles: Array[AbstractFile] = Array.empty
12+
protected def getSubDir(packageDirName: String): Option[AbstractFile] =
13+
Option(dir.lookupName(packageDirName, directory = true))
14+
protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match {
15+
case Some(f) => dir.iterator.filter(f).toArray
16+
case _ => dir.toArray
17+
}
18+
def getName(f: AbstractFile): String = f.name
19+
def toAbstractFile(f: AbstractFile): AbstractFile = f
20+
def isPackage(f: AbstractFile): Boolean = f.isPackage
21+
22+
// mimic the behavior of the old nsc.util.DirectoryClassPath
23+
def asURLs: Seq[URL] = Seq(new URL(dir.name))
24+
def asClassPathStrings: Seq[String] = Seq(dir.path)
25+
26+
override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl
27+
28+
def findClassFile(className: String): Option[AbstractFile] = {
29+
val relativePath = FileUtils.dirPath(className)
30+
val classFile = new PlainFile(Path(s"$dir/$relativePath.class"))
31+
if (classFile.exists) Some(classFile)
32+
else None
33+
}
34+
35+
private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
36+
37+
protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file)
38+
protected def isMatchingFile(f: AbstractFile): Boolean = f.isClass
39+
}

0 commit comments

Comments
 (0)