Skip to content

Commit

Permalink
fix: change project ref setting to support VSCode workspace folder re…
Browse files Browse the repository at this point in the history
…load
  • Loading branch information
kasiaMarek committed Jul 8, 2024
1 parent dd8736c commit 4f6101b
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,51 @@
package scala.meta.internal.metals

import scala.util.Try

import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath

import ujson.Value

object DelegateSetting {
private val settingName = "delegate"
val delegateSetting = "delegate"
private val projectRefSetting = "project-ref"

def writeDeleteSetting(
def writeProjectRef(
folder: AbsolutePath,
servicePath: AbsolutePath,
projectRefs: List[AbsolutePath],
): Unit = {
val relPath = servicePath.toRelative(folder)
val jsonText = ujson.Obj(settingName -> relPath.toString()).toString()
val relPath = projectRefs.map(_.toRelative(folder))
val jsonText =
ujson.Obj(projectRefSetting -> relPath.map(_.toString())).toString()
folder.resolve(Directories.metalsSettings).writeText(jsonText)
}

def readProjectRefs(folder: AbsolutePath): List[AbsolutePath] = {
for {
setting <- getSetting(folder, projectRefSetting).toList
ref <- setting.arrOpt.getOrElse(Nil)
pathStr <- ref.strOpt
path = folder.resolve(pathStr).dealias
if path.exists
} yield path
}

def readDeleteSetting(folder: AbsolutePath): Option[AbsolutePath] =
def readDeleteSetting(root: AbsolutePath): Option[AbsolutePath] =
for {
text <- folder.resolve(Directories.metalsSettings).readTextOpt
json = ujson.read(text)
relPath <- json(settingName).strOpt
path = folder.resolve(relPath).dealias
setting <- getSetting(root, delegateSetting)
relPath <- setting.strOpt
path = root.resolve(relPath).dealias
if path.exists
} yield path

private def getSetting(
root: AbsolutePath,
settingName: String,
): Option[Value] =
for {
text <- root.resolve(Directories.metalsSettings).readTextOpt
setting <- Try(ujson.read(text)(settingName)).toOption
} yield setting

}
Original file line number Diff line number Diff line change
Expand Up @@ -1422,26 +1422,6 @@ abstract class MetalsLspService(
}
}

protected def importBuild(session: BspSession): Future[Unit] = {
val importedBuilds0 = timerProvider.timed("Imported build") {
session.importBuilds()
}
for {
bspBuilds <- workDoneProgress.trackFuture(
Messages.importingBuild,
importedBuilds0,
)
_ = {
val idToConnection = bspBuilds.flatMap { bspBuild =>
val targets =
bspBuild.build.workspaceBuildTargets.getTargets().asScala
targets.map(t => (t.getId(), bspBuild.connection))
}
mainBuildTargetsData.resetConnections(idToConnection)
}
} yield compilers.cancel()
}

val buildClient: ForwardingMetalsBuildClient =
new ForwardingMetalsBuildClient(
languageClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,43 @@ class ProjectMetalsLspService(
.ignoreValue
}

def saveProjectReferencesInfo(bspBuilds: List[BspSession.BspBuild]): Unit = {
val projectRefs = bspBuilds
.flatMap { session =>
session.build.workspaceBuildTargets.getTargets().asScala.flatMap {
_.getBaseDirectory() match {
case null | "" => None
case path => path.toAbsolutePathSafe
}
}
}
.distinct
.filterNot(_.startWith(path))
if (projectRefs.nonEmpty)
DelegateSetting.writeProjectRef(path, projectRefs)
}

protected def importBuild(session: BspSession): Future[Unit] = {
val importedBuilds0 = timerProvider.timed("Imported build") {
session.importBuilds()
}
for {
bspBuilds <- workDoneProgress.trackFuture(
Messages.importingBuild,
importedBuilds0,
)
_ = {
val idToConnection = bspBuilds.flatMap { bspBuild =>
val targets =
bspBuild.build.workspaceBuildTargets.getTargets().asScala
targets.map(t => (t.getId(), bspBuild.connection))
}
mainBuildTargetsData.resetConnections(idToConnection)
saveProjectReferencesInfo(bspBuilds)
}
} yield compilers.cancel()
}

def slowConnectToBuildServer(
forceImport: Boolean
): Future[BuildChange] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ class WorkspaceFolders(
def initServices(folders: List[Folder]): WorkspaceFoldersServices = {
val (scalaProjects, nonScalaProjects) =
folders.partition(_.isMetalsProject)
val knowProjectRefs = scalaProjects.flatMap(_.projectReferences).toSet
val scalaServices =
scalaProjects
.filterNot(
_.optDelegatePath.exists(path => scalaProjects.exists(_.path == path))
.filterNot(path =>
path.optDelegatePath.exists(path =>
scalaProjects.exists(_.path == path)
) ||
knowProjectRefs(path.path)
)
.map(createService)
WorkspaceFoldersServices(scalaServices, nonScalaProjects)
Expand All @@ -49,13 +53,13 @@ class WorkspaceFolders(

allFolders.updateAndGet(_.filterNot(shouldBeRemoved) ++ toAdd)

val knowProjectRoots = toAdd.flatMap(_.projectReferences)
val actualToAdd = toAdd.filterNot { folder =>
findDelegate(getFolderServices.filterNot(shouldBeRemoved), folder) match {
case Some(service) =>
DelegateSetting.writeDeleteSetting(folder.path, service.path)
true
case Some(_) => true
case _ =>
folder.optDelegatePath.exists(path => toAdd.exists(_.path == path))
folder.optDelegatePath.exists(path => toAdd.exists(_.path == path)) &&
!knowProjectRoots.contains(folder.path)
}
}

Expand Down Expand Up @@ -131,16 +135,13 @@ class WorkspaceFolders(
private def findDelegate(
services: List[MetalsLspService],
folder: Folder,
): Option[MetalsLspService] =
folder.optDelegatePath
.flatMap(delegate => services.find(_.path == delegate))
.orElse {
val uri = folder.path.toURI
services.find(
_.buildTargets.all
.exists(_.baseDirectoryPath.map(_.toURI).contains(uri))
)
}
): Option[MetalsLspService] = {
val uri = folder.path.toURI
services.find(
_.buildTargets.all
.exists(_.baseDirectoryPath.map(_.toURI).contains(uri))
)
}
}

case class WorkspaceFoldersServices(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,9 @@ class Folder(
lazy val optDelegatePath: Option[AbsolutePath] =
DelegateSetting.readDeleteSetting(path)

def projectReferences: List[AbsolutePath] =
DelegateSetting.readProjectRefs(path)

def nameOrUri: String = visibleName.getOrElse(path.toString())
}

Expand Down
84 changes: 80 additions & 4 deletions tests/slow/src/test/scala/tests/sbt/WorkspaceFolderSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tests.sbt
import scala.meta.internal.builds.SbtBuildTool
import scala.meta.internal.builds.SbtDigest
import scala.meta.internal.metals.DelegateSetting
import scala.meta.internal.metals.Directories
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.{BuildInfo => V}
import scala.meta.io.AbsolutePath
Expand Down Expand Up @@ -119,8 +120,8 @@ class WorkspaceFolderSuite extends BaseImportSuite("sbt-workspace-suite") {
|""".stripMargin,
)
_ = assertEquals(
DelegateSetting.readDeleteSetting(workspace.resolve(libraryFolder)),
Some(workspace.resolve("main-folder")),
DelegateSetting.readProjectRefs(workspace.resolve("main-folder")),
List(workspace.resolve(libraryFolder)),
)
} yield ()
}
Expand Down Expand Up @@ -151,9 +152,9 @@ class WorkspaceFolderSuite extends BaseImportSuite("sbt-workspace-suite") {
|""".stripMargin
)

DelegateSetting.writeDeleteSetting(
workspace.resolve(libraryFolder),
DelegateSetting.writeProjectRef(
workspace.resolve("main-folder"),
List(workspace.resolve(libraryFolder)),
)

QuickBuild.bloopInstall(workspace.resolve(libraryFolder))
Expand Down Expand Up @@ -188,4 +189,79 @@ class WorkspaceFolderSuite extends BaseImportSuite("sbt-workspace-suite") {
)
} yield ()
}

test("open-delegating-service-old-setting") {
cleanWorkspace()
val libraryFolder = "library-folder"

writeLayout(
s"""|/$libraryFolder/project/build.properties
|sbt.version=${V.sbtVersion}
|/$libraryFolder/project/plugins.sbt
|addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "${V.sbtBloopVersion}")
|/$libraryFolder/build.sbt
|scalaVersion := "${V.scala213}"
|lazy val libraryProject = project.in(file("."))
|/$libraryFolder/metals.json
|{
| "libraryProject": {
| "scalaVersion": "${V.scala213}"
| }
|}
|/$libraryFolder/src/main/scala/example/A.scala
|package example
|object A {
| val i = 3
|}
|""".stripMargin
)

writeOldDelegateSetting(
workspace.resolve("main-folder"),
workspace.resolve(libraryFolder),
)

QuickBuild.bloopInstall(workspace.resolve(libraryFolder))

for {
_ <- initialize(
Map(
libraryFolder ->
"",
"main-folder" ->
s"""|/project/build.properties
|sbt.version=${V.sbtVersion}
|/build.sbt
|scalaVersion := "${V.scala213}"
|lazy val root = project.in(file(".")).dependsOn(ProjectRef(file("../$libraryFolder"), "libraryProject"))
|/src/main/scala/a/Main.scala
|package a
|import example.A
|object Main {
| val j: Int = A.i
|}
|""".stripMargin,
),
expectError = false,
)
_ <- server.didOpen("main-folder/src/main/scala/a/Main.scala")
_ = assertNoDiagnostics()
_ = assertEquals(
server.fullServer.folderServices.size,
1,
"should not create folder service for project ref",
)
} yield ()
}

private def writeOldDelegateSetting(
folder: AbsolutePath,
projectRef: AbsolutePath,
): Unit = {
val relPath = folder.toRelative(projectRef)
val jsonText = ujson
.Obj(DelegateSetting.delegateSetting -> relPath.toString())
.toString()
projectRef.resolve(Directories.metalsSettings).writeText(jsonText)
}
}

0 comments on commit 4f6101b

Please sign in to comment.