diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index 401bf6d31..b5006aabb 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -116,21 +116,18 @@ object BuildServerProtocol { val base = loadedBuildUnit.localBase val sbtFiles = configurationSources(base) val pluginData = loadedBuildUnit.unit.plugins.pluginData - val all = Vector.newBuilder[SourceItem] - def add(fs: Seq[File], sourceItemKind: Int, generated: Boolean): Unit = { - fs.foreach(f => all += (SourceItem(f.toURI, sourceItemKind, generated = generated))) - } - all += (SourceItem( - loadedBuildUnit.unit.plugins.base.toURI, - SourceItemKind.Directory, - generated = false - )) - add(pluginData.unmanagedSourceDirectories, SourceItemKind.Directory, generated = false) - add(pluginData.unmanagedSources, SourceItemKind.File, generated = false) - add(pluginData.managedSourceDirectories, SourceItemKind.Directory, generated = true) - add(pluginData.managedSources, SourceItemKind.File, generated = true) - add(sbtFiles, SourceItemKind.File, generated = false) - Value(SourcesItem(id, all.result())) + val dirs = pluginData.unmanagedSourceDirectories + val sourceFiles = getStandaloneSourceFiles(pluginData.unmanagedSources, dirs) + val managedDirs = pluginData.managedSourceDirectories + val managedSourceFiles = + getStandaloneSourceFiles(pluginData.managedSources, managedDirs) + val items = + dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++ + sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++ + managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++ + managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) ++ + sbtFiles.map(toSourceItem(SourceItemKind.File, generated = false)) + Value(SourcesItem(id, items.toVector)) } val successfulItems = anyOrThrow(items ++ buildItems) val result = SourcesResult(successfulItems.toVector) @@ -277,14 +274,14 @@ object BuildServerProtocol { bspBuildTargetSourcesItem := { val id = bspTargetIdentifier.value val dirs = unmanagedSourceDirectories.value - val managed = managedSources.value - val items = (dirs.toVector map { dir => - SourceItem(dir.toURI, SourceItemKind.Directory, generated = false) - }) ++ - (managed.toVector map { x => - SourceItem(x.toURI, SourceItemKind.File, generated = true) - }) - SourcesItem(id, items) + val sourceFiles = getStandaloneSourceFiles(unmanagedSources.value, dirs) + val managedDirs = managedSourceDirectories.value + val managedSourceFiles = getStandaloneSourceFiles(managedSources.value, managedDirs) + val items = dirs.map(toSourceItem(SourceItemKind.Directory, generated = false)) ++ + sourceFiles.map(toSourceItem(SourceItemKind.File, generated = false)) ++ + managedDirs.map(toSourceItem(SourceItemKind.Directory, generated = true)) ++ + managedSourceFiles.map(toSourceItem(SourceItemKind.File, generated = true)) + SourcesItem(id, items.toVector) }, bspBuildTargetResourcesItem := { val id = bspTargetIdentifier.value @@ -456,6 +453,18 @@ object BuildServerProtocol { } } + private def getStandaloneSourceFiles( + sourceFiles: Seq[File], + sourceDirs: Seq[File] + ): Seq[File] = { + sourceFiles.filterNot { f => + sourceDirs.exists(dir => f.toPath.startsWith(dir.toPath)) + } + } + + private def toSourceItem(itemKind: Int, generated: Boolean)(file: File): SourceItem = + SourceItem(file.toURI, itemKind, generated) + private def checkMetalsCompatibility( semanticdbEnabled: Boolean, semanticdbVersion: String, diff --git a/server-test/src/server-test/buildserver/BaseSource.scala b/server-test/src/server-test/buildserver/BaseSource.scala new file mode 100644 index 000000000..3359bd24f --- /dev/null +++ b/server-test/src/server-test/buildserver/BaseSource.scala @@ -0,0 +1 @@ +object BaseSource diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index 47aad7b31..5aa231752 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -10,8 +10,13 @@ package testpkg import sbt.internal.bsp._ import sbt.internal.langserver.ErrorCodes import sbt.IO +import sbt.internal.protocol.JsonRpcRequestMessage +import sbt.internal.protocol.codec.JsonRPCProtocol._ +import sjsonnew.JsonWriter +import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter } import java.io.File +import java.net.URI import java.nio.file.Paths import scala.concurrent.duration._ @@ -47,41 +52,43 @@ object BuildServerTest extends AbstractServerTest { test("buildTarget/sources") { _ => val buildTarget = buildTargetUri("util", "Compile") val badBuildTarget = buildTargetUri("badBuildTarget", "Compile") - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "24", "method": "buildTarget/sources", "params": { - | "targets": [{ "uri": "$buildTarget" }, { "uri": "$badBuildTarget" }] - |} }""".stripMargin - ) + svr.sendJsonRpc(buildTargetSources(24, Seq(buildTarget, badBuildTarget))) assert(processing("buildTarget/sources")) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri) assert(sources.contains(new File(svr.baseDirectory, "util/src/main/scala").toURI)) } - test("buildTarget/sources SBT") { _ => - val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build" - svr.sendJsonRpc( - s"""{ "jsonrpc": "2.0", "id": "25", "method": "buildTarget/sources", "params": { - | "targets": [{ "uri": "$x" }] - |} }""".stripMargin + test("buildTarget/sources: base sources") { _ => + val buildTarget = buildTargetUri("buildserver", "Compile") + svr.sendJsonRpc(buildTargetSources(25, Seq(buildTarget))) + assert(processing("buildTarget/sources")) + val s = svr.waitFor[SourcesResult](10.seconds) + val sources = s.items.head.sources + val expectedSource = SourceItem( + new File(svr.baseDirectory, "BaseSource.scala").toURI, + SourceItemKind.File, + generated = false ) + assert(sources.contains(expectedSource)) + } + test("buildTarget/sources: sbt") { _ => + val x = new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#buildserver-build") + svr.sendJsonRpc(buildTargetSources(26, Seq(x))) assert(processing("buildTarget/sources")) val s = svr.waitFor[SourcesResult](10.seconds) val sources = s.items.head.sources.map(_.uri).sorted val expectedSources = Vector( "build.sbt", - "project/", "project/A.scala", "project/src/main/java", "project/src/main/scala-2", "project/src/main/scala-2.12", "project/src/main/scala-sbt-1.0", "project/src/main/scala/", - "project/src/main/scala/B.scala", "project/target/scala-2.12/sbt-1.0/src_managed/main" ).map(rel => new File(svr.baseDirectory.getAbsoluteFile, rel).toURI).sorted assert(sources == expectedSources) } - test("buildTarget/compile") { _ => val buildTarget = buildTargetUri("util", "Compile") svr.sendJsonRpc( @@ -421,8 +428,20 @@ object BuildServerTest extends AbstractServerTest { } } - private def buildTargetUri(project: String, config: String): String = - s"${svr.baseDirectory.getAbsoluteFile.toURI}#$project/$config" + private def buildTargetSources(id: Int, buildTargets: Seq[URI]): String = { + val targets = buildTargets.map(BuildTargetIdentifier.apply).toVector + request(id, "buildTarget/sources", SourcesParams(targets)) + } + + private def request[T: JsonWriter](id: Int, method: String, params: T): String = { + val request = JsonRpcRequestMessage("2.0", id.toString, method, Converter.toJson(params).get) + val json = Converter.toJson(request).get + CompactPrinter(json) + } + + private def buildTargetUri(project: String, config: String): URI = { + new URI(s"${svr.baseDirectory.getAbsoluteFile.toURI}#$project/$config") + } private def metaBuildTarget: String = s"${svr.baseDirectory.getAbsoluteFile.toURI}project/#buildserver-build/Compile"