-
-
Notifications
You must be signed in to change notification settings - Fork 390
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes #3015 There are several limitations implied by `intellijBSP` client which led to this implementation: mainly: 1. There is no way to exclude a directory which is not inside a module `content root` aka `baseDirectory` (at least I didnt find a way - excluding `.bsp` is hardcoded in `intellijBSP`). 2. `Intellij` won't include content root of module if it has no sources defined (this might be a bug in `intellijBSP`). This implies that there should be at least one module which content root is equal to project root. In some sitiations such module is defined by user in `build.sc`, but not always. In other cases, a `SyntheticRootBspBuildTargetData` with `bspTarget.baseDirectory` == `topLevelProjectRoot` would be created to handle exclusions. I went for a solution that is very local to `bsp` module. Alternative solutions I though about: - make RootModule extend BSPModule - create synthetic `RootModule with BspModule` either during bootstrap if there is no module with `BspTarget.baseDirectory` = `topLevelProjectDirectory` - create synthetic `RootModule with BspModule` in bspWorker `State` However personally I think that alternative solutions seem overkill for achieving such simple effect as excluding directories. How it works: 1. `MillBuildServer.State` contains additional field - `Option[SyntheticRootBspBuildTargetData]` `SyntheticRootBspBuildTargetData` contains minimal data required for `BuildTarget` to be reported by`def workspaceBuildTargets` (forced by limitation 1). 2. this field will be `Some` only if there is no normal module with `BspBuildTarget.contentRoot `== `topLevelProjectRoot` aka. module which can exclude topLevel folders 3. if `SyntheticRootBspBuildTargetData` is created, it is reported in `def workspaceBuildTargets` together with other modules, but filtered out from other bspClient requests (exceptions: `buildOutputPaths`, and `buildTargetSources`) 4. `def buildTargetOutputPaths` - for a module which has `BspBuildTarget.contentRoot `== `topLevelProjectRoot` (so potentially `SyntheticRootBspBuildTargetData`) i `os.walk` from `topLevelProjectRoot` looking for `build.sc` files and in their folders I exclude ".idea",".bsp","out",".bloop". - os.walk ensures exclusion of folders in nested mill projects note: following paths are excluded from walk ```scala def ignore(path: os.Path): Boolean = { path.last.startsWith(".") || path.endsWith(os.RelPath("out")) || path.endsWith(os.RelPath("target")) || path.endsWith(os.RelPath("docs")) || path.endsWith(os.RelPath("mill-build")) } ``` Actually if there was no limitations I mentioned, this would be the only required step. 5. Because of `limitation 2` i had to report some source for `SyntheticRootBspBuildTargetData` - chosen "src", but this can be any, even non existing path. Tested manually: - Navigation of sources - Navigation of meta-builds - Navigation of build.sc - excluded folders appear in IDE - solution does not collide with user-made RootModule in build.sc (actually it has to be a RootModule with JavaModule to be seen by BSP) --------- Co-authored-by: Li Haoyi <[email protected]>
- Loading branch information
1 parent
6c49d7f
commit 5041301
Showing
7 changed files
with
179 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
bsp/worker/src/mill/bsp/worker/SyntheticRootBspBuildTargetData.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package mill.bsp.worker | ||
|
||
import ch.epfl.scala.bsp4j.{BuildTargetIdentifier, SourceItem, SourceItemKind, SourcesItem} | ||
import mill.bsp.worker.Utils.{makeBuildTarget, sanitizeUri} | ||
import mill.scalalib.bsp.{BspBuildTarget, BspModule} | ||
import mill.scalalib.bsp.BspModule.Tag | ||
|
||
import java.util.UUID | ||
import scala.jdk.CollectionConverters._ | ||
import ch.epfl.scala.bsp4j.BuildTarget | ||
|
||
/** | ||
* Synthesised [[BspBuildTarget]] to handle exclusions. | ||
* Intellij-Bsp doesn't provide a way to exclude files outside of module,so if there is no module having content root of [[topLevelProjectRoot]], [[SyntheticRootBspBuildTargetData]] will be created | ||
*/ | ||
class SyntheticRootBspBuildTargetData(topLevelProjectRoot: os.Path) { | ||
val id: BuildTargetIdentifier = new BuildTargetIdentifier( | ||
Utils.sanitizeUri(topLevelProjectRoot / s"synth-build-target-${UUID.randomUUID()}") | ||
) | ||
|
||
val bt: BspBuildTarget = BspBuildTarget( | ||
displayName = Some(topLevelProjectRoot.last + "-root"), | ||
baseDirectory = Some(topLevelProjectRoot), | ||
tags = Seq(Tag.Manual), | ||
languageIds = Seq.empty, | ||
canCompile = false, | ||
canTest = false, | ||
canRun = false, | ||
canDebug = false | ||
) | ||
|
||
val target: BuildTarget = makeBuildTarget(id, Seq.empty, bt, None) | ||
private val sourcePath = topLevelProjectRoot / "src" | ||
def synthSources = new SourcesItem( | ||
id, | ||
Seq(new SourceItem(sanitizeUri(sourcePath), SourceItemKind.DIRECTORY, false)).asJava | ||
) // intellijBSP does not create contentRootData for module with only outputPaths (this is probably a bug) | ||
} | ||
object SyntheticRootBspBuildTargetData { | ||
def makeIfNeeded( | ||
existingModules: Iterable[BspModule], | ||
workspaceDir: os.Path | ||
): Option[SyntheticRootBspBuildTargetData] = { | ||
def containsWorkspaceDir(path: Option[os.Path]) = path.exists(workspaceDir.startsWith) | ||
if (existingModules.exists { m => containsWorkspaceDir(m.bspBuildTarget.baseDirectory) }) None | ||
else Some(new SyntheticRootBspBuildTargetData(workspaceDir)) | ||
} | ||
} |
Oops, something went wrong.