Skip to content

Commit

Permalink
replace java streams with generators
Browse files Browse the repository at this point in the history
Previously, we created a wrapper for file walking and children listing
to automatically close the underlying java stream, but it lacked the
flexibility and ease of use of standard collections.
Now, we use the geny library to close the stream automatically. It also provides a
subset of scala collection methods (forall, map, etc.)
  • Loading branch information
Olafur Pall Geirsson authored and mzarnowski committed Sep 23, 2019
1 parent 9932570 commit b54291b
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 159 deletions.
6 changes: 6 additions & 0 deletions build.sbt
Expand Up @@ -172,6 +172,11 @@ lazy val interfaces = project
)
)

val genyVersion = Def.setting {
if (scalaVersion.value.startsWith("2.11")) "0.1.6"
else "0.1.8"
}

lazy val mtags = project
.settings(
moduleName := "mtags",
Expand All @@ -189,6 +194,7 @@ lazy val mtags = project
"com.thoughtworks.qdox" % "qdox" % "2.0-M9", // for java mtags
"org.jsoup" % "jsoup" % "1.11.3", // for extracting HTML from javadocs
"org.lz4" % "lz4-java" % "1.6.0", // for streaming hashing when indexing classpaths
"com.lihaoyi" %% "geny" % genyVersion.value,
"org.scalameta" % "semanticdb-scalac-core" % V.scalameta cross CrossVersion.full
),
libraryDependencies ++= {
Expand Down
9 changes: 3 additions & 6 deletions metals/src/main/scala/scala/meta/internal/builds/Digest.scala
Expand Up @@ -11,7 +11,6 @@ import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath
import scala.util.control.NonFatal
import scala.xml.Node
import scala.meta.internal.mtags.ListFiles

case class Digest(
md5: String,
Expand Down Expand Up @@ -61,11 +60,9 @@ object Digest {
): Boolean = {
if (!path.isDirectory) true
else {
var success = true
ListFiles.foreach(path) { file =>
success &= digestFile(file, digest)
path.list.forall { file =>
digestFile(file, digest)
}
success
}
}

Expand Down Expand Up @@ -122,7 +119,7 @@ object Digest {
chldrenSuccessful.forall(p => p)
}
try {
val xml = XML.loadFile(file.toNIO.toFile())
val xml = XML.loadFile(file.toNIO.toFile)
digestElement(xml)
xml.text.split("\\s+").foreach(word => digest.update(word.getBytes))
true
Expand Down
@@ -1,9 +1,8 @@
package scala.meta.internal.builds

import scala.meta.io.AbsolutePath
import scala.meta.internal.mtags.WalkFiles
import java.security.MessageDigest
import scala.meta.internal.mtags.ListFiles
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath

object GradleDigest extends Digestable {
override protected def digestWorkspace(
Expand All @@ -23,33 +22,28 @@ object GradleDigest extends Digestable {
}

def digestBuildSrc(path: AbsolutePath, digest: MessageDigest): Boolean = {
WalkFiles.foreach(path) { file =>
path.walk.forall { file =>
Digest.digestFile(file, digest)
}
true
}

def digestSubProjects(
workspace: AbsolutePath,
digest: MessageDigest
): Boolean = {
val directories = ListFiles(workspace).filter(_.isDirectory)
val directories = workspace.list.filter(_.isDirectory).toList

val (subprojects, dirs) = directories.partition { file =>
ListFiles
.exists(file) { path =>
val stringPath = path.toString
stringPath.endsWith(".gradle") || stringPath.endsWith("gradle.kts")
}
file.list.exists { path =>
val stringPath = path.toString
stringPath.endsWith(".gradle") || stringPath.endsWith("gradle.kts")
}
}
/*
If a dir contains a gradle file we need to treat is as a workspace
*/
val isSuccessful = subprojects.forall { file =>
digestWorkspace(
file,
digest
)
digestWorkspace(file, digest)
}

/*
Expand Down
Expand Up @@ -2,20 +2,19 @@ package scala.meta.internal.builds

import java.security.MessageDigest
import scala.meta.io.AbsolutePath
import scala.meta.internal.mtags.WalkFiles
import scala.meta.internal.mtags.MtagsEnrichments._
import scala.meta.internal.metals.MetalsEnrichments._

object MavenDigest extends Digestable {
override protected def digestWorkspace(
workspace: AbsolutePath,
digest: MessageDigest
): Boolean = {
Digest.digestFile(workspace.resolve("pom.xml"), digest)
WalkFiles.foreach(workspace) { file =>
if (file.filename == "pom.xml") {
workspace.walk.forall {
case file if file.filename == "pom.xml" =>
Digest.digestFile(file, digest)
}
case _ =>
true
}
true
}
}
12 changes: 4 additions & 8 deletions metals/src/main/scala/scala/meta/internal/builds/SbtDigest.scala
Expand Up @@ -5,7 +5,6 @@ import java.security.MessageDigest
import scala.meta.internal.builds.Digest.digestScala
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath
import scala.meta.internal.mtags.ListFiles

object SbtDigest extends Digestable {

Expand All @@ -20,14 +19,11 @@ object SbtDigest extends Digestable {
Digest.digestDirectory(project.resolve("project"), digest)
}

def digestSbtFiles(
path: AbsolutePath,
digest: MessageDigest
): Boolean = {
if (!path.isDirectory) {
true
def digestSbtFiles(path: AbsolutePath, digest: MessageDigest): Boolean = {
if (path.isDirectory) {
path.list.forall(file => digestSbtFile(digest)(file))
} else {
ListFiles.forall(path)(file => digestSbtFile(digest)(file))
true
}
}

Expand Down
Expand Up @@ -14,7 +14,6 @@ import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.mtags.MD5
import scala.meta.io.AbsolutePath
import scala.util.Try
import scala.meta.internal.mtags.ListFiles

/**
* Implements BSP server discovery, named "BSP Connection Protocol" in the spec.
Expand Down Expand Up @@ -125,7 +124,7 @@ final class BspServers(
val buf = List.newBuilder[AbsolutePath]
def visit(dir: AbsolutePath): Unit =
if (dir.isDirectory) {
ListFiles.foreach(dir) { p =>
dir.list.foreach { p =>
if (p.extension == "json") {
buf += p
}
Expand Down
Expand Up @@ -294,21 +294,13 @@ object MetalsEnrichments
}
}

def exists: Boolean = {
Files.exists(path.toNIO)
}

def touch(): Unit = {
if (!path.exists) {
parent.createDirectories()
path.parent.createDirectories()
Files.createFile(path.toNIO)
}
}

def parent: AbsolutePath = {
AbsolutePath(path.toNIO.getParent)
}

def createDirectories(): AbsolutePath = {
AbsolutePath(Files.createDirectories(dealias.toNIO))
}
Expand All @@ -318,10 +310,9 @@ object MetalsEnrichments
}

def writeText(text: String): Unit = {
parent.createDirectories()
path.parent.createDirectories()
Files.write(path.toNIO, text.getBytes(StandardCharsets.UTF_8))
}

}

implicit class XtensionString(value: String) {
Expand Down
Expand Up @@ -1316,7 +1316,7 @@ class MetalsLanguageServer(
private def indexWorkspaceSources(): Unit = {
for {
(sourceItem, targets) <- buildTargets.sourceItemsToBuildTargets
source <- WalkFiles(sourceItem)
source <- sourceItem.walk
if source.isScalaOrJava
} {
targets.asScala.foreach { target =>
Expand Down
29 changes: 14 additions & 15 deletions mtags/src/main/scala/scala/meta/internal/metals/JdkSources.scala
@@ -1,16 +1,16 @@
package scala.meta.internal.metals

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import scala.meta.io.AbsolutePath
import scala.meta.io.RelativePath
import scala.meta.internal.mtags.MtagsEnrichments._

/**
* Locates zip file on disk that contains the source code for the JDK.
*/
object JdkSources {
private val sources = Paths.get("src.zip")
private val libSources = Paths.get("lib").resolve(sources)
private val sources = RelativePath(Paths.get("src.zip"))
private val libSources = RelativePath(Paths.get("lib")).resolve(sources)

def apply(userJavaHome: Option[String] = None): Option[AbsolutePath] = {
candidates(userJavaHome).headOption
Expand All @@ -23,33 +23,32 @@ object JdkSources {
}

private def candidates(userJavaHome: Option[String]): List[AbsolutePath] = {
def isJdkCandidate(path: Path): Boolean = {
def containsJre = Files.exists(path.resolve("jre"))
val name = path.getFileName.toString
def isJdkCandidate(path: AbsolutePath): Boolean = {
def containsJre = path.resolve("jre").exists
val name = path.filename.toString
name.contains("jdk") || containsJre //e.g. jdk-8, java-openjdk-11
}

for {
javaHomeString <- userJavaHome.orElse(defaultJavaHome).toList
javaHome = Paths.get(javaHomeString)
javaHome = AbsolutePath(Paths.get(javaHomeString))
jdkHome = {
if (isJdkCandidate(javaHome)) {
Nil
} else {
// In case java.home points to the JRE instead of the JDK,
// try to find jdk among its siblings
Files
.list(javaHome.getParent)
javaHome.parent.list
.filter(isJdkCandidate)
.toArray[Path](size => new Array(size))
.sortBy(_.getFileName)
.toArray[AbsolutePath]
.sortBy(_.filename)
.toList
}
}
jdk <- jdkHome ++ List(javaHome.getParent, javaHome)
jdk <- jdkHome ++ List(javaHome.parent, javaHome)
src <- List(sources, libSources).map(jdk.resolve)
if Files.isRegularFile(src)
} yield AbsolutePath(src)
if src.isFile
} yield src
}

def getOrThrow(): AbsolutePath = {
Expand Down
Expand Up @@ -20,7 +20,6 @@ import scala.util.control.NonFatal
import scala.util.Properties
import java.nio.file.FileSystems
import java.net.URI
import scala.meta.internal.mtags.ListFiles

/**
* An index to lookup classfiles contained in a given classpath.
Expand Down Expand Up @@ -137,7 +136,7 @@ class PackageIndex() {
for {
pkg <- Files.newDirectoryStream(dir).iterator().asScala
symbol = pkg.toString.stripPrefix("/packages/").replace('.', '/') + "/"
absoluteModuleLink <- ListFiles(AbsolutePath(pkg))
absoluteModuleLink <- AbsolutePath(pkg).list
} {
val moduleLink = absoluteModuleLink.toNIO
val module =
Expand Down
41 changes: 0 additions & 41 deletions mtags/src/main/scala/scala/meta/internal/mtags/ListFiles.scala

This file was deleted.

Expand Up @@ -3,13 +3,15 @@ package scala.meta.internal.mtags
import com.google.gson.Gson
import com.google.gson.JsonElement
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util
import java.util.Optional
import java.util.concurrent.CancellationException
import java.util.logging.Level
import java.util.logging.Logger
import geny.Generator
import org.eclipse.lsp4j.CompletionItem
import org.eclipse.lsp4j.MarkupContent
import org.eclipse.lsp4j.jsonrpc.CancelChecker
Expand Down Expand Up @@ -359,4 +361,31 @@ trait MtagsEnrichments {
def isVar: Boolean = (properties & p.VAR.value) != 0
def isVal: Boolean = (properties & p.VAL.value) != 0
}

implicit class XtensionStream[A](stream: java.util.stream.Stream[A]) {
import scala.collection.JavaConverters._
def asScala: Generator[A] = {
Generator.selfClosing((stream.iterator.asScala, () => stream.close()))
}
}

implicit class XtensionAbsolutePath(path: AbsolutePath) {
def parent: AbsolutePath = {
AbsolutePath(path.toNIO.getParent)
}

def exists: Boolean = {
Files.exists(path.toNIO)
}

def walk: Generator[AbsolutePath] = {
if (exists) Files.walk(path.toNIO).asScala.map(AbsolutePath(_))
else Generator()
}

def list: Generator[AbsolutePath] = {
if (exists) Files.walk(path.toNIO).asScala.map(AbsolutePath(_))
else Generator()
}
}
}

0 comments on commit b54291b

Please sign in to comment.