-
-
Notifications
You must be signed in to change notification settings - Fork 364
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
Better Metals support - Automatic SemanticDB enablement (RFC) #1546
Comments
I think an extra con to this one is that if you have a team where some users are using IntelliJ and some are using Metals we don't want to penalize IntelliJ users with Metals specific stuff. So right off the batt Between b and c, I'm not fully sure what fits Mill better, so I'll only speak from the Metals perspective, but I'm always in favor of there being as little as friction as possible for the user. Ideally, they shouldn't even notice that anything different is happening. I've been playing around a bit with Metals just being able to generate the bsp config for Mill automatically in scalameta/metals@cb3e60b and it's not a ton of work, and while doing it, it got me thinking that on the Metals side we do have a lot of control over what happens when a user chooses to use a specific BSP server. For example in both sbt and Bloop we are adding plugins to the build to either ensure we can export to Bloop or ensure that stuff is set correctly in sbt for semanticdb production. Just throwing out ideas here, but what if we ran I think on the Metals side we are totally open to whatever fits Mill best and whatever causes the least friction for users and allows for the actual build to not need to change for teams that are mixed editor teams. |
The proper (read Mill-idiomatic) way is to just have dedicated targets/tasks to compile for Metals. That would be the cleanest and most robust solution. It may include proxying the whole modules or writing to a custom |
This would mean compiling the code twice which is a waste of resources and against one of the benefits of using BSP. |
So thinking about this some more. When a BSP initialize request comes in, could we just detect the |
It's not about detection of Metals. This one is actually easy, and we do it e.g. when we calculate the sources of the It's about tracking changes and keeping caches up-to-date and reproducible. Whenever we detect, that we want to enable SemanticDB and some extra scalac options, we need to also rebuild. Same, when we decide to disable it. That's why I proposed cii), in which we detect Metals and persist that fact (e.g. in a dot-file or a persitent target). As long as we find this fact |
It would be very nice, if we could produce semanticDB data in a separate process, which is less resource intensive as full compilation. From a tooling perspective this is the most wanted setup. Maybe it just works and we can stop after a specific compiler phase and get the semanticDB data at a significant shorter compile time. If such a setup is possible, it would be my go-to implementation. |
I made some experiments. Turns out, starting the scala compiler with semanticDB enabled but stopping it after the |
I pushed my current experiment to PR #1977. I won't continue it for at least a week... |
If I'm understanding the code right: case m: SemanticDbScalaModule => T.task {
m.compile()
// we also generate semantic db data
m.semanticDbData()
()
} I know caching is a concern here, and so is speed, but doing it this way will make it even slower won't it? We're doing a full compile here and then also doing a I need to dig into the Metals side a bit more, but testing out the pr, it still doesn't recognize that semanticDB is enabled and you can see this for example via BSP {
"target": {
"uri": "file:///Users/ckipp/Documents/scala-workspace/minimal/minimal?id\u003dminimal"
},
"options": [
"-Xplugin:/Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/acyclic_2.13.8/0.3.3/acyclic_2.13.8-0.3.3.jar"
],
"classpath": [
"file:///Users/ckipp/Documents/scala-workspace/minimal/minimal/resources",
"file:///Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/acyclic_2.13.8/0.3.3/acyclic_2.13.8-0.3.3.jar",
"file:///Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar"
],
"classDirectory": "file:///Users/ckipp/Documents/scala-workspace/minimal/out/minimal/compile.dest/classes"
} Compare this to the same thing with Bloop: {
"target": {
"uri": "file:/Users/ckipp/Documents/scala-workspace/minimal/minimal/?id\u003dminimal"
},
"options": [
"-P:semanticdb:sourceroot:/Users/ckipp/Documents/scala-workspace/minimal",
"-Xplugin:/Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/acyclic_2.13.8/0.3.3/acyclic_2.13.8-0.3.3.jar",
"-Yrangepos",
"-P:semanticdb:failures:warning",
"-P:semanticdb:synthetics:on",
"-Xplugin-require:semanticdb",
"-Xplugin:/Users/ckipp/Library/Caches/bloop/semanticdb/org.scalameta.semanticdb-scalac_2.13.8.4.5.9/semanticdb-scalac_2.13.8-4.5.9.jar"
],
"classpath": [
"file:///Users/ckipp/Documents/scala-workspace/minimal/.bloop/out/minimal/bloop-bsp-clients-classes/classes-Metals-69AMmR6cQMakFe1OX8uwKg\u003d\u003d/",
"file:///Users/ckipp/Library/Caches/bloop/semanticdb/com.sourcegraph.semanticdb-javac.0.7.4/semanticdb-javac-0.7.4.jar",
"file:///Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/lihaoyi/acyclic_2.13.8/0.3.3/acyclic_2.13.8-0.3.3.jar",
"file:///Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.8/scala-library-2.13.8.jar"
],
"classDirectory": "file:///Users/ckipp/Documents/scala-workspace/minimal/.bloop/out/minimal/bloop-bsp-clients-classes/classes-Metals-69AMmR6cQMakFe1OX8uwKg\u003d\u003d/"
} I do see the new task being called... [Trace - 00:28:00 pm] Received notification 'build/taskProgress'
Params: {
"taskId": {
"id": "-793638209"
},
"eventTime": 1659090480520,
"message": "[5/18] minimal.scalaCompilerClasspath \u003e [47/48] mill.bsp.MillBuildServer.mill-build.semanticDbData ",
"total": 18,
"progress": 5,
"unit": "minimal.scalaCompilerClasspath"
}
[Trace - 00:28:00 pm] Received notification 'build/showMessage'
Params: {
"type": 4,
"message": "effective scalac options: List(-Xplugin:/Users/ckipp/.ivy2/local/com.lihaoyi/mill-main-moduledefs_2.13/0.10.5-56-eca162/jars/mill-main-moduledefs_2.13.jar, -deprecation, -Xplugin:/Users/ckipp/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scalameta/semanticdb-scalac_2.13.8/4.5.11/semanticdb-scalac_2.13.8-4.5.11.jar, -Yrangepos, -P:semanticdb:sourceroot:/Users/ckipp/Documents/scala-workspace/minimal, -Ystop-after:semanticdb-typer)"
} Although there is also the message being given back to the user which I'm unsure about. When this task runs, where does it spit out the semanticdb? I don't see a |
@ckipp01 Thanks for this early feedback. As described in the PR, this is a not yet finished experiment whether this works at all. The task item "Use SemanticDB in BSP module" isn't checked yet, it's still an open TODO. But regarding your first questions of "understanding". Compiling with SemanticDB is slower than without (around 30% for Scala 2 as states here: #1536 (comment)), so Ideally, Metals is only invoking the There are for sure some subtle details we need to work out, e.g. the right place to apply additional compiler flags required to produce better message in the IDE. |
I don't know zinc internals enough, but if zinc supports incremental compilation with the |
Ha, sorry some of my comments were probably premature 😆 I just saw the pr and got real excited to try it.
Yea, this is what I was referring to. I was just curious of the cost compared to just one compile with the plugin enabled. However, thinking more about it, you're totally right. The fact that this still enables the separation of producing semanticdb and also compilation while preserving cache for the cli is probably a win and pretty clever.
Yea, I don't think there is even a good way to determine when it would be needed unless we do some sort of comparison on class files or something, but then it's just faster to probably reproduce. In theory every compilation will trigger it. As you continue to iterate don't hesitate to ping me to test stuff with Metals or look into things. If this is the direction we go, we may need to change our doctor report a bit for Mill BSP since the response of |
Correct me if I'm wrong but I think that if you use T.task {
m.compile()
// we also generate semantic db data
m.semanticDbData()
()
} |
Exactly. Yet, I think this concrete piece of code is suspected to change. All named targets can be scheduled on different threads. At least, in the current parallel implementation. |
One difficulty is, that BSP only want a single classes directory. And Metals is searching for semanticDB files in that directory. As we have two different targets to produce class files and semanticDB files, we result with two distinct output directories. As a consequence, we need to copy all files to a third location and use this as the output directory. |
@ckipp01 I updated my PR #1977 (comment) and would appreciate your feedback. I think we hit that predicted issue, as Metals "thinks" semanticDB isn't enabled. |
For a nice user experience Metals (a Scala Language Server which connects to Mill via BSP) needs also SemanticDB information, which can be generated by enabling the semanticdb-scalac-plugin for Scala modules.
We need to find a good integration and implement it.
Summary from PR #1536
Originally posted by @lefou in #1536 (comment)
Mill has various IDE support:
** IDEA, which supports Languages Java and Scala
** Metals as Language Server used by many different editors, supports only Language Scala; requires SemanticDB generator
As SemanticDB generation isn't cheap in terms of processing time and may have unforseen side-effects, we only want to enable it if it is in the interest of the user, which means: only enable it when the user is using Metals.
Three options to handle SemanticDB:
a) leaf it to the user to enable it in their build, e.g. use
ScalaMetalsSupport
trait.pros:
cons:
b) have a dedicated compile target for Metals (we could detect it in the BSP initialize request or by env var)
pros:
cons:
c) automatically enable SemanticDB in case the user uses Metals
pros:
cons:
scalacOptions
,scalacPluginIvyDeps
, ...How could we implement c):
i) depend on external info, e.g. existence of
.metals
directory or some file in it.pros:
cons:
build.sc
(and the actual mill args and cache) as single source of truthii) persist the fact that we were once executed from Metals
pros:
mill clean
ormill clean <persistent-target>
cons:
The text was updated successfully, but these errors were encountered: