-
Notifications
You must be signed in to change notification settings - Fork 319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Go to definition in classpath #23
Changes from 1 commit
ab517c3
6547cf3
eebbe39
445e332
062f8e9
cc537c5
766361a
864821d
4b175b9
6ad69f0
bf1b6f9
420f11d
e64a505
5c7951a
bcd3905
01f51b3
db1779a
3de1c85
da8e1d8
dca5a4a
2d09433
a85dbaf
fe8cfa4
12a4a64
0b29617
d05108d
c2cb7bd
020faaa
81b8520
dc03def
ed8e327
fd2035a
50191c1
3576919
5e9272f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,38 +5,78 @@ import java.nio.file.Files | |
import java.util.Properties | ||
import com.typesafe.scalalogging.LazyLogging | ||
import org.langmeta.io.AbsolutePath | ||
import org.langmeta.io.Classpath | ||
|
||
/** | ||
* Configuration to load up a presentation compiler. | ||
* | ||
* In sbt, one compiler config typically corresponds to one project+config. | ||
* For example one sbt project with test/main/it configurations has three | ||
* CompilerConfig. | ||
* | ||
* @param sources list of source files for this project | ||
* @param scalacOptions space separated list of flags to pass to the Scala compiler | ||
* @param dependencyClasspath File.pathSeparated list of *.jar and classDirectories. | ||
* Includes both dependencyClasspath and classDirectory. | ||
* @param classDirectory The output directory where *.class files are emitted | ||
* for this project. | ||
* @param sourceJars File.pathSeparated list of *-sources.jar from the | ||
* dependencyClasspath. | ||
*/ | ||
case class CompilerConfig( | ||
sources: List[AbsolutePath], | ||
scalacOptions: List[String], | ||
classpath: String, | ||
libraryDependencies: List[ModuleID] | ||
) | ||
classDirectory: AbsolutePath, | ||
dependencyClasspath: List[AbsolutePath], | ||
sourceJars: List[AbsolutePath] | ||
) { | ||
override def toString: String = | ||
s"CompilerConfig(" + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we have pprint as a dependency, you can probably just do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't find |
||
s"sources={+${sources.length}}, " + | ||
s"scalacOptions=${scalacOptions.mkString(" ")}, " + | ||
s"dependencyClasspath={+${dependencyClasspath.length}}, " + | ||
s"classDirectory=$classDirectory, " + | ||
s"sourceJars={+${sourceJars.length}})" | ||
def classpath: String = | ||
(classDirectory :: dependencyClasspath).mkString(File.pathSeparator) | ||
} | ||
|
||
object CompilerConfig extends LazyLogging { | ||
|
||
def fromPath( | ||
path: AbsolutePath | ||
)(implicit cwd: AbsolutePath): CompilerConfig = { | ||
val input = Files.newInputStream(path.toNIO) | ||
try { | ||
val props = new Properties() | ||
props.load(input) | ||
val sources = props | ||
.getProperty("sources") | ||
.split(File.pathSeparator) | ||
.iterator | ||
.map(AbsolutePath(_)) | ||
.toList | ||
val scalacOptions = props.getProperty("scalacOptions").split(" ").toList | ||
val classpath = props.getProperty("classpath") | ||
val libraryDependencies = | ||
ModuleID.fromString(props.getProperty("libraryDependencies")) | ||
CompilerConfig( | ||
sources, | ||
scalacOptions, | ||
classpath, | ||
libraryDependencies.toList | ||
) | ||
fromProperties(props) | ||
} finally input.close() | ||
} | ||
|
||
def fromProperties( | ||
props: Properties | ||
)(implicit cwd: AbsolutePath): CompilerConfig = { | ||
val sources = props | ||
.getProperty("sources") | ||
.split(File.pathSeparator) | ||
.iterator | ||
.map(AbsolutePath(_)) | ||
.toList | ||
val scalacOptions = | ||
props.getProperty("scalacOptions").split(" ").toList | ||
val dependencyClasspath = | ||
Classpath(props.getProperty("dependencyClasspath")).shallow | ||
val sourceJars = | ||
Classpath(props.getProperty("sourceJars")).shallow | ||
val classDirectory = | ||
AbsolutePath(props.getProperty("classDirectory")) | ||
CompilerConfig( | ||
sources, | ||
scalacOptions, | ||
classDirectory, | ||
dependencyClasspath, | ||
sourceJars | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package scala.meta.languageserver | ||
|
||
/** | ||
* The ScalametaLanguageServer effects. | ||
* | ||
* Observable[Unit] is not descriptive of what the observable represents. | ||
* Instead, we create Unit-like types to better document what effects are | ||
* flowing through our application. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Love this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Poor man's type-safety. I refactored to this in fact because I hit on bugs related to accidentally creating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are these phantom types? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really since those values also exist at runtime. I'm still not sure if this was necessary, maybe we can avoid it by using stricter scalac options (no discard unit). However, I think it's a nice replacement for |
||
*/ | ||
sealed abstract class Effects | ||
object Effects { | ||
final class IndexSemanticdb extends Effects | ||
final val IndexSemanticdb = new IndexSemanticdb | ||
final class IndexSourcesClasspath extends Effects | ||
final val IndexSourcesClasspath = new IndexSourcesClasspath | ||
final class InstallPresentationCompiler extends Effects | ||
final val InstallPresentationCompiler = new InstallPresentationCompiler | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,16 @@ import java.nio.file.Files | |
import java.nio.file.Path | ||
import java.nio.file.SimpleFileVisitor | ||
import java.nio.file.attribute.BasicFileAttributes | ||
import java.text.DecimalFormat | ||
import java.text.NumberFormat | ||
import java.util.concurrent.TimeUnit | ||
import java.util.concurrent.atomic.AtomicInteger | ||
import java.util.zip.ZipInputStream | ||
import scala.collection.GenSeq | ||
import scala.collection.parallel.mutable.ParArray | ||
import scala.meta.parsers.ParseException | ||
import scala.reflect.ClassTag | ||
import scala.util.Sorting | ||
import scala.util.control.NonFatal | ||
import com.typesafe.scalalogging.LazyLogging | ||
import org.langmeta.inputs.Input | ||
|
@@ -37,22 +43,47 @@ object Ctags extends LazyLogging { | |
* Build an index from a classpath of -sources.jar | ||
* | ||
* @param shouldIndex An optional filter to skip particular relative filenames. | ||
* @param inParallel If true, use parallel collection to index using all | ||
* available CPU power. If false, uses single-threaded | ||
* collection. | ||
* @param callback A callback that is called as soon as a document has been | ||
* indexed. | ||
*/ | ||
def index( | ||
classpath: List[AbsolutePath], | ||
shouldIndex: RelativePath => Boolean = _ => true, | ||
inParallel: Boolean = true | ||
shouldIndex: RelativePath => Boolean = _ => true | ||
)(callback: Document => Unit): Unit = { | ||
val fragments = allClasspathFragments(classpath, inParallel) | ||
val fragments = allClasspathFragments(classpath) | ||
val totalIndexedFiles = new AtomicInteger() | ||
val totalIndexedLines = new AtomicInteger() | ||
val start = System.nanoTime() | ||
def elapsed: Long = | ||
TimeUnit.SECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS) | ||
val decimal = new DecimalFormat("###.###") | ||
val N = fragments.length | ||
def updateTotalLines(doc: Document): Unit = doc.input match { | ||
case Input.VirtualFile(_, contents) => | ||
// NOTE(olafur) it would be interesting to have separate statistics for | ||
// Java/Scala lines/s but that would require a more sophisticated setup. | ||
totalIndexedLines.addAndGet(countLines(contents)) | ||
case _ => | ||
} | ||
def reportProgress(indexedFiles: Int): Unit = { | ||
val percentage = ((indexedFiles / N.toDouble) * 100).toInt | ||
val loc = decimal.format(totalIndexedLines.get() / elapsed) | ||
logger.info( | ||
s"Progress $percentage%, ${decimal.format(indexedFiles)} files indexed " + | ||
s"out of total ${decimal.format(fragments.length)} ($loc loc/s)" | ||
) | ||
} | ||
logger.info(s"Indexing $N source files") | ||
fragments.foreach { fragment => | ||
try { | ||
val indexedFiles = totalIndexedFiles.incrementAndGet() | ||
if (indexedFiles % 200 == 0) { | ||
reportProgress(indexedFiles) | ||
} | ||
if (shouldIndex(fragment.name)) { | ||
callback(index(fragment)) | ||
val doc = index(fragment) | ||
updateTotalLines(doc) | ||
callback(doc) | ||
} | ||
} catch { | ||
case _: ParseException => // nothing | ||
|
@@ -79,7 +110,7 @@ object Ctags extends LazyLogging { | |
logger.trace(s"Indexing ${input.path} with length ${input.value.length}") | ||
val indexer: CtagsIndexer = | ||
if (isScala(input.path)) ScalaCtags.index(input) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what would it take to index There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not much really, I can give some pointers if you open an issue 😉 |
||
else if (isJava(input.path)) JavaCtags.index(input) | ||
else if (isJava(input.path)) QDoxCtags.index(input) | ||
else { | ||
throw new IllegalArgumentException( | ||
s"Unknown file extension ${input.path}" | ||
|
@@ -96,7 +127,9 @@ object Ctags extends LazyLogging { | |
) | ||
} | ||
|
||
private def canIndex(path: String): Boolean = isScala(path) || isJava(path) | ||
private def canIndex(path: String): Boolean = | ||
// isScala(path) || | ||
isJava(path) | ||
private def isJava(path: String): Boolean = path.endsWith(".java") | ||
private def isScala(path: String): Boolean = path.endsWith(".scala") | ||
private def isScala(path: Path): Boolean = PathIO.extension(path) == "scala" | ||
|
@@ -112,11 +145,8 @@ object Ctags extends LazyLogging { | |
*/ | ||
private def allClasspathFragments( | ||
classpath: List[AbsolutePath], | ||
inParallel: Boolean | ||
): GenSeq[Fragment] = { | ||
var buf = | ||
if (inParallel) ParArray.newBuilder[Fragment] | ||
else List.newBuilder[Fragment] | ||
): ParArray[Fragment] = { | ||
var buf = ParArray.newBuilder[Fragment] | ||
classpath.foreach { base => | ||
def exploreJar(base: AbsolutePath): Unit = { | ||
val stream = Files.newInputStream(base.toNIO) | ||
|
@@ -167,6 +197,27 @@ object Ctags extends LazyLogging { | |
// Skip | ||
} | ||
} | ||
buf.result() | ||
val result = buf.result() | ||
Sorting.stableSort(result.arrayseq)( | ||
implicitly[ClassTag[Fragment]], | ||
Ordering.by { fragment => | ||
PathIO.extension(fragment.name.toNIO) match { | ||
case "scala" => 1 | ||
case "java" => 2 | ||
case _ => 3 | ||
} | ||
} | ||
) | ||
result | ||
} | ||
|
||
private def countLines(string: String): Int = { | ||
var i = 0 | ||
var lines = 0 | ||
while (i < string.length) { | ||
if (string.charAt(i) == '\n') lines += 1 | ||
i += 1 | ||
} | ||
lines | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this import for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unused, removed.