Is your feature request related to a problem? Please describe.
Some scala developers want to use ENSIME with scala-cli.
It seems in the best interests of the scala community that these tools are compatible with each other.
(I’m creating this ticket by proxy, I’m not a scala-cli user.)
Describe the solution you’d like
Ideally, the support would be automatic and baked in to the tool.
If the ensime jar exists for the current version of scala then include it as a compiler plugin, and ensure that all the sources are downloaded for all the coursier dependencies.
Alternatively, it would be good for users to opt in using some custom code that could be copy/pasted into their user configuration.
Ideally, the decision to use ensime would be based on the user rather than the file, since the lack of the ensime jar would make the files non-portable. However, as a last resort, having the ability to support local jar files that can reference the version of scala used by the project would be a suitable workaround.
Describe alternatives you’ve considered
https://scala-cli.virtuslab.org/docs/reference/directives/#compiler-plugins requires compiler plugins to be published ivy or maven style, which would require users to locally publish a custom assembly jar, which would be a lot of work for them.
Additional context
One of the features of the ensime compiler is to hijack the global.reporter
during the compile. I would like to understand how scala-cli sets and uses the Reporter
to understand if this would cause a problem. Every invocation compile
in sbt will result in a fresh Reporter
being used, which is ideal, but if the same reporter is reused then it might cause problems for this feature.
References
Example plugins for sbt and mill can be found in the .tar.gz
at https://ensime.github.io/ copied here for convenience
package ensime
import sbt._
import Keys._
object EnsimePlugin extends AutoPlugin {
override def trigger = allRequirements
object autoImport {
val ensimeJar = taskKey[File]("Location of ENSIME")
}
import autoImport._
override lazy val globalSettings: Seq[Setting[_]] = Seq(
// perhaps an overreach...
transitiveClassifiers := Seq("sources")
)
override lazy val projectSettings: Seq[Setting[_]] = Seq(
ensimeJar := (Def.taskDyn {
val jar = file(s"""${sys.props("user.home")}/.cache/ensime/lib/ensime-${scalaVersion.value}.jar""")
if (jar.isFile) Def.task {
// make sure the user always has sources if ENSIME is enabled
val _ = updateClassifiers.value
jar
} else Def.task {
streams.value.log.warn(s"ENSIME not found. Try\n\n sbt ++${scalaVersion.value}! install\n\nin the ensime-tng repo.")
jar
}
}).value,
scalacOptions ++= {
val jar = ensimeJar.value
if (jar.isFile) Seq(s"-Xplugin:${jar.getAbsolutePath}")
else Seq()
},
clean := {
val _ = clean.value
// clears the reverse lookup of source files to the target
file(s"""${sys.props("user.home")}/.cache/ensime${target.value}""").delete(): Unit
}
)
}
import mill._
import mill.scalalib._
// To install this Mill plugin, add
//
// ```
// import $file.plugins.Ensime
// import Ensime.EnsimeModule
//
// object ... extends EnsimeModule
// ```
//
// to your build.sc
trait EnsimeModule extends ScalaModule { module =>
def ensimeJar : T[Option[PathRef]] = T.input {
val jar = os.home / ".cache" / "ensime" / "lib" / s"""ensime-${scalaVersion()}.jar"""
val ensimeWanted = os.exists(os.home / ".cache" / "ensime")
if (os.isFile(jar) && ensimeWanted) {
// make sure the user always has sources if ENSIME is enabled
val fetchTask = fetchDepSources()
fetchTask()
Some(PathRef(jar))
} else {
if (ensimeWanted){
T.ctx.log.error(s"ENSIME not found. Try\n\n sbt ++${scalaVersion()}! install\n\nin the ensime-tng repo.")
}
None
}
}
override def scalacOptions = T {
super.scalacOptions() ++ {ensimeJar() match {
case Some(jar) => Seq(s"-Xplugin:${jar.path.toIO.getAbsolutePath}")
case None => Seq()
}}
}
private def fetchDepSources: mill.define.Task[() => Unit] = T.task {
import coursier._
import coursier.util._
import scala.concurrent.ExecutionContext.Implicits.global
val repos = module.repositoriesTask()
val allIvyDeps = module.transitiveIvyDeps() ++ module.transitiveCompileIvyDeps()
val coursierDeps = allIvyDeps.map(module.resolveCoursierDependency()).toList
val withSources = Resolution(
coursierDeps
.map(d =>
d.withAttributes(
d.attributes.withClassifier(coursier.Classifier("sources"))
)
)
.toSeq
)
() => {
val fetch = ResolutionProcess.fetch(repos, coursier.cache.Cache.default.fetch)
val _ = withSources.process.run(fetch).unsafeRun()
}
}
}
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:13 (7 by maintainers)
Top GitHub Comments
Ok so I think ideal scenario here and what this issue can address is: Add a global configuration option that would do
ensimeEnable=true/false
. It would add the plugin if it exists at a certain path to the compiler if the versions match and download sources by default. If anyone feels there is a better way let me know.However, I don’t think we will be working on it any time soon unless there is a significant interest. We would be happy to accept outside contribution if there is anyone that wants to help out.
As the current workaround the users should:
//> using options "-Xplugin:/home/user/.cache/ensime/lib/ensime-2.13.17.jar"
"org:::name:version,classifier=sources"
I want to setup a feature requests list similar to the one we have in Metals, but I haven’t yet worked on it. So I would say voting on it, yeah.