Skip to content

Commit

Permalink
Make BSP 1st class citizen (#969)
Browse files Browse the repository at this point in the history
* Make BSP 1st class citizen

* Improve mill build classpath

Pull request: #969
  • Loading branch information
joan38 authored Oct 21, 2020
1 parent 0801414 commit 2f9b633
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
package mill.contrib
package mill.bsp

import ch.epfl.scala.bsp4j.BuildClient
import java.io.PrintWriter
import java.nio.file.FileAlreadyExistsException
import java.util.concurrent.Executors
import ch.epfl.scala.bsp4j._
import mill._
import mill.contrib.bsp.MillBuildServer
import mill.{BuildInfo, T}
import mill.bsp.{BspConfigJson, MillBuildServer}
import mill.define.{Command, Discover, ExternalModule}
import mill.eval.Evaluator
import mill.modules.Util
import org.eclipse.lsp4j.jsonrpc.Launcher
import upickle.default._
import scala.collection.JavaConverters._
import scala.concurrent.CancellationException
import scala.util.Try

case class BspConfigJson(
name: String,
argv: Seq[String],
millVersion: String,
bspVersion: String,
languages: Seq[String]
) extends BspConnectionDetails(name, argv.asJava, millVersion, bspVersion, languages.asJava)

object BspConfigJson {
implicit val rw: ReadWriter[BspConfigJson] = macroRW
}
import upickle.default.write

object BSP extends ExternalModule {

implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]()

lazy val millDiscover: Discover[BSP.this.type] = Discover[this.type]
lazy val millDiscover: Discover[this.type] = Discover[this.type]
val bspProtocolVersion = "2.0.0"
val languages = Seq("scala", "java")

Expand Down Expand Up @@ -73,15 +58,16 @@ object BSP extends ExternalModule {
def createBspConnectionJson(): String = {
val millPath = sys.props
.get("java.class.path")
.getOrElse(throw new IllegalStateException("System property java.class.path not set"))
.getOrElse(throw new IllegalStateException(
"System property java.class.path not set"))

write(
BspConfigJson(
"mill-bsp",
Seq(
"sh",
"-c",
s"env ${sys.env.map { case (k, v) => s""""$k=$v"""" }.toSeq.mkString(" ")} $millPath -i mill.contrib.BSP/start"
s"env ${sys.env.map { case (k, v) => s""""$k=$v"""" }.toSeq.mkString(" ")} $millPath -i ${BSP.getClass.getCanonicalName.split("\\$").last}/start"
),
Util.millProperty("MILL_VERSION").getOrElse(BuildInfo.millVersion),
bspProtocolVersion,
Expand Down Expand Up @@ -113,7 +99,10 @@ object BSP extends ExternalModule {
ev.env,
false
)
val millServer = new MillBuildServer(evaluator, bspProtocolVersion, BuildInfo.millVersion)
val millServer = new MillBuildServer(
evaluator,
bspProtocolVersion,
BuildInfo.millVersion)
val executor = Executors.newCachedThreadPool()

val stdin = System.in
Expand Down
17 changes: 17 additions & 0 deletions bsp/src/mill/bsp/BspConfigJson.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package mill.bsp

import ch.epfl.scala.bsp4j.BspConnectionDetails
import scala.jdk.CollectionConverters._
import upickle.default._

case class BspConfigJson(
name: String,
argv: Seq[String],
millVersion: String,
bspVersion: String,
languages: Seq[String]
) extends BspConnectionDetails(name, argv.asJava, millVersion, bspVersion, languages.asJava)

object BspConfigJson {
implicit val rw: ReadWriter[BspConfigJson] = macroRW
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mill.contrib.bsp
package mill.bsp

import java.io.File
import java.util.concurrent.ConcurrentHashMap
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mill.contrib.bsp
package mill.bsp

import java.io.{PrintWriter, StringWriter}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mill.contrib.bsp
package mill.bsp

import ch.epfl.scala.bsp4j._
import mill.api.Logger
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package mill.contrib.bsp
package mill.bsp

import ammonite.runtime.SpecialClassLoader
import ch.epfl.scala.bsp4j._
import com.google.gson.JsonObject
import java.util.concurrent.CompletableFuture
import mill._
import mill.api.{DummyTestReporter, Result, Strict}
import mill.contrib.bsp.ModuleUtils._
import mill.contrib.bsp.Utils._
import mill.bsp.ModuleUtils._
import mill.bsp.Utils._
import mill.define.Segment.Label
import mill.define.{Discover, ExternalModule}
import mill.eval.Evaluator
Expand Down Expand Up @@ -133,12 +133,9 @@ class MillBuildServer(evaluator: Evaluator, bspVersion: String, serverVersion: S

val items = dependencySourcesParams.getTargets.asScala
.foldLeft(Seq.empty[DependencySourcesItem]) { (items, targetId) =>
val all = if (targetId == millBuildTargetId) {
Try(getClass.getClassLoader.asInstanceOf[SpecialClassLoader]).fold(
_ => Seq.empty,
_.allJars.filter(url => isSourceJar(url) && exists(Path(url.getFile))).map(_.toURI.toString)
)
} else {
val all = if (targetId == millBuildTargetId)
getMillBuildClasspath(evaluator, source = true)
else {
val module = getModule(targetId, modules)
val sources = evaluateInformativeTask(
evaluator,
Expand Down Expand Up @@ -375,10 +372,7 @@ class MillBuildServer(evaluator: Evaluator, bspVersion: String, serverVersion: S
.foldLeft(Seq.empty[ScalacOptionsItem]) { (items, targetId) =>
val newItem =
if (targetId == millBuildTargetId) {
val classpath = Try(getClass.getClassLoader.asInstanceOf[SpecialClassLoader]).fold(
_ => Seq.empty,
_.allJars.filter(url => !isSourceJar(url) && exists(Path(url.getFile))).map(_.toURI.toString)
)
val classpath = getMillBuildClasspath(evaluator, source = false)
Some(new ScalacOptionsItem(
targetId,
Seq.empty.asJava,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package mill.contrib.bsp
package mill.bsp

import ammonite.runtime.SpecialClassLoader
import ch.epfl.scala.bsp4j._
import java.net.URL
import java.net.URLClassLoader
import mill._
import mill.api.Result.Success
import mill.api.{PathRef, Strict}
Expand All @@ -13,7 +15,9 @@ import mill.scalalib.api.Util
import mill.scalalib.{JavaModule, ScalaModule, TestModule}
import mill.scalanativelib._
import mill.util.Ctx
import os.{Path, exists}
import scala.collection.JavaConverters._
import scala.util.Try

/**
* Utilities for translating the mill build into
Expand Down Expand Up @@ -101,6 +105,17 @@ object ModuleUtils {
target
}

def getMillBuildClasspath(evaluator: Evaluator, source: Boolean): Seq[String] = {
val all = Try(evaluator.rootModule.getClass.getClassLoader.asInstanceOf[SpecialClassLoader]).fold(
_ => Seq.empty,
_.allJars
)
val filtered =
if (source) all.filter(url => isSourceJar(url))
else all.filter(url => !isSourceJar(url))
filtered.filter(url => exists(Path(url.getFile))).map(_.toURI.toString)
}

/**
* Compute the BuildTarget associated with the given mill
* JavaModule, which is any module present in the working
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mill.contrib.bsp
package mill.bsp

import ch.epfl.scala.bsp4j.{BuildTargetIdentifier, CompileParams, RunParams, TestParams}

Expand Down Expand Up @@ -124,4 +124,4 @@ object TaskParameters {
def fromTestParams(testParams: TestParams): Parameters = {
TParams(testParams)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package mill.contrib.bsp
package mill.bsp

import ch.epfl.scala.bsp4j._
import mill._
import mill.api.Result.{Skipped, Success}
import mill.api.{BuildProblemReporter, Result}
import mill.contrib.bsp.ModuleUtils._
import mill.bsp.ModuleUtils._
import mill.eval.Evaluator
import mill.modules.Jvm
import mill.scalalib.Lib.discoverTests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package mill.contrib.bsp
package mill.bsp

import mill.util.ScriptTestSuite
import os._
import utest._

object BspInstallTests extends ScriptTestSuite(false) {
override def workspaceSlug: String = "gen-idea-hello-world"
override def scriptSourcePath: Path = os.pwd / "contrib" / "bsp" / "test" / "resources" / workspaceSlug
override def scriptSourcePath: Path = os.pwd / "bsp" / "test" / "resources" / workspaceSlug

def tests: Tests = Tests {
"BSP install" - {
val workspacePath = initWorkspace()
eval("mill.contrib.BSP/install")
eval("mill.bsp.BSP/install")

assert(exists(workspacePath / ".bsp" /"mill.json"))
assert(exists(workspacePath / ".bsp" / "mill.json"))
}
}
}
23 changes: 10 additions & 13 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ object Deps {
def scalaReflect(scalaVersion: String) = ivy"org.scala-lang:scala-reflect:${scalaVersion}"
def scalacScoveragePlugin = ivy"org.scoverage::scalac-scoverage-plugin:1.4.1"
val sourcecode = ivy"com.lihaoyi::sourcecode:0.2.1"
val ujsonCirce = ivy"com.lihaoyi::ujson-circe:1.2.0"
val upickle = ivy"com.lihaoyi::upickle:1.2.0"
val upickle = ivy"com.lihaoyi::upickle:1.2.1"
val utest = ivy"com.lihaoyi::utest:0.7.4"
val zinc = ivy"org.scala-sbt::zinc:1.4.0-M1"
val bsp = ivy"ch.epfl.scala:bsp4j:2.0.0-M4"
Expand Down Expand Up @@ -480,16 +479,6 @@ object contrib extends MillModule {
def testArgs = T(scalanativelib.testArgs())
}

object bsp extends MillModule {

override def compileModuleDeps = Seq(scalalib, scalajslib, main, scalanativelib)
def ivyDeps = Agg(
Deps.bsp,
Deps.ujsonCirce,
Deps.sbtTestInterface
)
}

object artifactory extends MillModule {
override def compileModuleDeps = Seq(scalalib)
}
Expand Down Expand Up @@ -548,6 +537,14 @@ object scalanativelib extends MillModule {
}
}

object bsp extends MillModule {
override def compileModuleDeps = Seq(scalalib, scalajslib, scalanativelib)
def ivyDeps = Agg(
Deps.bsp,
Deps.sbtTestInterface
)
}

def testRepos = T{
Seq(
"MILL_ACYCLIC_REPO" ->
Expand Down Expand Up @@ -677,7 +674,7 @@ def launcherScript(shellJvmArgs: Seq[String],
}

object dev extends MillModule{
def moduleDeps = Seq(scalalib, scalajslib, scalanativelib)
def moduleDeps = Seq(scalalib, scalajslib, scalanativelib, bsp)


def forkArgs =
Expand Down
11 changes: 9 additions & 2 deletions docs/pages/1 - Intro to Mill.md
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,16 @@ mill mill.scalalib.Dependency/showUpdates
mill mill.scalalib.Dependency/showUpdates --allowPreRelease true # also show pre-release versions
```

## IntelliJ Support
## IDE Support

Mill supports IntelliJ by default. Use `mill mill.scalalib.GenIdea/idea` to
Mill supports any IDE that is compatible with [BSP](https://build-server-protocol.github.io/), such as IntelliJ.
Use `mill mill.bsp.BSP/install` to generate the BSP project config for your build.

It also enables Intellij to provide navigation & code-completion features within your build file itself.

## IntelliJ Support (legacy)

Mill supports IntelliJ configuration generation. Use `mill mill.scalalib.GenIdea/idea` to
generate an IntelliJ project config for your build.

This also configures IntelliJ to allow easy navigate & code-completion within
Expand Down
19 changes: 3 additions & 16 deletions docs/pages/9 - Contrib Modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,24 +175,11 @@ object project extends BuildInfo {

## BSP - Build Server Protocol

The contrib.bsp module was created in order to integrate the Mill build tool
with IntelliJ IDEA via the Build Server Protocol (BSP). It implements most of
the server side functionality described in BSP, and can therefore connect to a
BSP client, including the one behind IntelliJ IDEA. This allows a lot of mill
tasks to be executed from the IDE.

### Importing an existing mill project in IntelliJ via BSP

1) Add the following import statement in the `build.sc` of your project:

```scala
import $ivy.`com.lihaoyi::mill-contrib-bsp:$MILL_VERSION`
```

2) Run the following command in the working directory of your project:
The contrib.bsp module is now included in mill by default and will eventually replace GenIdea.

Run the following command in the working directory of your project to generate the BSP config:
```
mill mill.contrib.BSP/install
mill mill.bsp.BSP/install
```

### Known Issues:
Expand Down

0 comments on commit 2f9b633

Please sign in to comment.