Skip to content

Commit

Permalink
no workspace support (scalameta#6289)
Browse files Browse the repository at this point in the history
* improvement: support multiple scala-cli servers

* improvement: support single files

* single file test

* cap number of scala-cli servers

* small adjustments

* for single files use temp workspace

* test adjustments

* review changes

* fix gradle test

* make `fallbackService`  lazy

* Don't look for semanticdb on path for scala-cli single files.

* add doc comment and change type

* post rebase fixes
  • Loading branch information
kasiaMarek authored Apr 23, 2024
1 parent cdc11c6 commit ff9d08a
Show file tree
Hide file tree
Showing 19 changed files with 567 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class NewProjectProvider(
config: ClientConfiguration,
shell: ShellRunner,
icons: Icons,
workspace: AbsolutePath,
workspace: () => AbsolutePath,
)(implicit context: ExecutionContext) {

private val templatesUrl =
Expand Down Expand Up @@ -69,7 +69,7 @@ class NewProjectProvider(
}

def createNewProjectFromTemplate(javaHome: Option[String]): Future[Unit] = {
val base = workspace.parent
val base = workspace().parent
val withTemplate = askForTemplate(
NewProjectProvider.curatedTemplates(icons)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,14 @@ final class BuildTargets private (
dataLock.synchronized {
this.data = BuildTargets.DataSeq(data :: this.data.list)
}

def removeData(data: TargetData): Unit =
dataLock.synchronized {
this.data match {
case BuildTargets.DataSeq(list) =>
BuildTargets.DataSeq(list.filterNot(_ == data))
}
}
}

object BuildTargets {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package scala.meta.internal.metals

import java.nio.file.Files
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.atomic.AtomicReference

import scala.concurrent.ExecutionContextExecutorService
import scala.concurrent.Future

import scala.meta.internal.builds.ShellRunner
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.clients.language.ConfiguredLanguageClient
import scala.meta.internal.metals.doctor.HeadDoctor
import scala.meta.io.AbsolutePath

import org.eclipse.lsp4j.DidCloseTextDocumentParams
import org.eclipse.lsp4j.InitializeParams

class FallbackMetalsLspService(
ec: ExecutionContextExecutorService,
sh: ScheduledExecutorService,
serverInputs: MetalsServerInputs,
languageClient: ConfiguredLanguageClient,
initializeParams: InitializeParams,
clientConfig: ClientConfiguration,
statusBar: StatusBar,
focusedDocument: () => Option[AbsolutePath],
shellRunner: ShellRunner,
timerProvider: TimerProvider,
initTreeView: () => Unit,
folder: AbsolutePath,
folderVisibleName: Option[String],
headDoctor: HeadDoctor,
workDoneProgress: WorkDoneProgress,
bspStatus: BspStatus,
) extends MetalsLspService(
ec,
sh,
serverInputs,
languageClient,
initializeParams,
clientConfig,
statusBar,
focusedDocument,
shellRunner,
timerProvider,
initTreeView,
folder,
folderVisibleName,
headDoctor,
bspStatus,
workDoneProgress,
maxScalaCliServers = 10,
) {

buildServerPromise.success(())
indexingPromise.success(())

private val files: AtomicReference[Set[AbsolutePath]] = new AtomicReference(
Set.empty
)

override def maybeImportScript(path: AbsolutePath): Option[Future[Unit]] = {
if (!path.isScala) None
else {
val prev = files.getAndUpdate(_ + path)
if (prev.contains(path)) None
else Some(scalaCli.start(path))
}
}

override def didClose(params: DidCloseTextDocumentParams): Unit = {
val path = params.getTextDocument.getUri.toAbsolutePath
files.getAndUpdate(_ - path)
super.didClose(params)
scalaCli.stop(path)
}

}

object FallbackMetalsLspService {
def path(): AbsolutePath = {
val uri = Files.createTempDirectory(s"fallback-service")
scribe.debug(s"creating tmp directory $uri for fallback service in")
uri.toFile.deleteOnExit()
AbsolutePath(uri)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scala.meta.internal.metals
import java.nio.charset.Charset

import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.scalacli.ScalaCliServers
import scala.meta.internal.mtags.Md5Fingerprints
import scala.meta.internal.mtags.SemanticdbClasspath
import scala.meta.internal.mtags.Semanticdbs
Expand All @@ -19,12 +20,16 @@ final class FileSystemSemanticdbs(
charset: Charset,
mainWorkspace: AbsolutePath,
fingerprints: Md5Fingerprints,
scalaCliServers: => ScalaCliServers,
) extends Semanticdbs {

override def textDocument(file: AbsolutePath): TextDocumentLookup = {
if (
(!file.toLanguage.isScala && !file.toLanguage.isJava) ||
file.toNIO.getFileSystem != mainWorkspace.toNIO.getFileSystem
file.toNIO.getFileSystem != mainWorkspace.toNIO.getFileSystem ||
scalaCliServers.loadedExactly(
file
) // scala-cli single files, interactive is used for those
) {
TextDocumentLookup.NotFound(file)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import scala.util.Try

import scala.meta.internal.builds.SbtBuildTool
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.scalacli.ScalaCliServers
import scala.meta.internal.mtags.MD5
import scala.meta.internal.mtags.Semanticdbs
import scala.meta.internal.mtags.Shebang
Expand All @@ -36,6 +37,7 @@ final class InteractiveSemanticdbs(
semanticdbIndexer: () => SemanticdbIndexer,
javaInteractiveSemanticdb: Option[JavaInteractiveSemanticdb],
buffers: Buffers,
scalaCliServers: => ScalaCliServers,
) extends Cancelable
with Semanticdbs {

Expand Down Expand Up @@ -76,6 +78,7 @@ final class InteractiveSemanticdbs(
source.isSbt || // sbt files
source.isWorksheet || // worksheets
doesNotBelongToBuildTarget || // standalone files
scalaCliServers.loadedExactly(source) || // scala-cli single files
sourceText.exists(
_.startsWith(Shebang.shebang)
) // starts with shebang
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import scala.meta.internal.metals.findfiles._
import scala.meta.internal.metals.formatting.OnTypeFormattingProvider
import scala.meta.internal.metals.formatting.RangeFormattingProvider
import scala.meta.internal.metals.newScalaFile.NewFileProvider
import scala.meta.internal.metals.scalacli.ScalaCli
import scala.meta.internal.metals.scalacli.ScalaCliServers
import scala.meta.internal.metals.testProvider.BuildTargetUpdate
import scala.meta.internal.metals.testProvider.TestSuitesProvider
import scala.meta.internal.metals.watcher.FileWatcher
Expand Down Expand Up @@ -135,6 +135,7 @@ class MetalsLspService(
headDoctor: HeadDoctor,
bspStatus: BspStatus,
workDoneProgress: WorkDoneProgress,
maxScalaCliServers: Int,
) extends Folder(folder, folderVisibleName, isKnownMetalsProject = true)
with Cancelable
with TextDocumentService {
Expand Down Expand Up @@ -360,6 +361,7 @@ class MetalsLspService(
charset,
folder,
fingerprints,
scalaCli,
)

private val interactiveSemanticdbs: InteractiveSemanticdbs = {
Expand All @@ -377,6 +379,7 @@ class MetalsLspService(
() => semanticDBIndexer,
javaInteractiveSemanticdb,
buffers,
scalaCli,
)
)
}
Expand Down Expand Up @@ -1173,6 +1176,7 @@ class MetalsLspService(
.inverseSources(path)
.getOrElse(current)
}
scalaCli.didFocus(path)
// Don't trigger compilation on didFocus events under cascade compilation
// because save events already trigger compile in inverse dependencies.
if (path.isDependencySource(folder)) {
Expand Down Expand Up @@ -2312,11 +2316,16 @@ class MetalsLspService(
val connOpt = buildTargets.buildServerOf(change.getTarget)
connOpt.nonEmpty && connOpt == ammonite.buildServer
}
val (scalaCliBuildChanges, otherChanges0) =
otherChanges.partition { change =>
val connOpt = buildTargets.buildServerOf(change.getTarget)
connOpt.nonEmpty && connOpt == scalaCli.buildServer
}

val scalaCliServers = scalaCli.servers
val groupedByServer = otherChanges.groupBy { change =>
val connOpt = buildTargets.buildServerOf(change.getTarget)
connOpt.flatMap(conn => scalaCliServers.find(_ == conn))
}
val scalaCliAffectedServers = groupedByServer.collect {
case (Some(server), _) => server
}
val mainConnectionChanges = groupedByServer.get(None)

if (ammoniteChanges.nonEmpty)
ammonite.importBuild().onComplete {
Expand All @@ -2325,17 +2334,21 @@ class MetalsLspService(
scribe.error("Error re-importing Ammonite build", exception)
}

if (scalaCliBuildChanges.nonEmpty)
scalaCli
scalaCliAffectedServers.map { server =>
server
.importBuild()
.onComplete {
case Success(()) =>
case Failure(exception) =>
scribe
.error("Error re-importing Scala CLI build", exception)
.error(
s"Error re-importing for a Scala CLI build with path ${server.path}",
exception,
)
}
}

if (otherChanges0.nonEmpty) {
if (mainConnectionChanges.nonEmpty) {
bspSession match {
case None => scribe.warn("No build server connected")
case Some(session) =>
Expand All @@ -2346,7 +2359,6 @@ class MetalsLspService(
focusedDocument().foreach(path => compilations.compileFile(path))
}
}

}
}

Expand All @@ -2365,7 +2377,7 @@ class MetalsLspService(
case other => Future.successful(other)
}

val scalaCliPath = scalaCli.path
val scalaCliPaths = scalaCli.paths

isConnecting.set(true)
(for {
Expand All @@ -2391,12 +2403,13 @@ class MetalsLspService(
case None =>
Future.successful(BuildChange.None)
}
_ <- scalaCliPath
.collectFirst {
case path if (!conflictsWithMainBsp(path.toNIO)) =>
scalaCli.start(path)
}
.getOrElse(Future.successful(()))
_ <- Future.sequence(
scalaCliPaths
.collect {
case path if (!conflictsWithMainBsp(path.toNIO)) =>
scalaCli.start(path)
}
)
_ = initTreeView()
} yield result)
.recover { case NonFatal(e) =>
Expand Down Expand Up @@ -2477,8 +2490,8 @@ class MetalsLspService(
}
}

val scalaCli: ScalaCli = register(
new ScalaCli(
val scalaCli: ScalaCliServers = register(
new ScalaCliServers(
() => compilers,
compilations,
workDoneProgress,
Expand All @@ -2491,9 +2504,10 @@ class MetalsLspService(
() => clientConfig.initialConfig,
() => userConfig,
parseTreesAndPublishDiags,
buildTargets,
maxScalaCliServers,
)
)
buildTargets.addData(scalaCli.buildTargetsData)

private val indexer = Indexer(
() => workspaceReload,
Expand Down Expand Up @@ -2521,12 +2535,13 @@ class MetalsLspService(
ammonite.buildTargetsData,
ammonite.lastImportedBuild,
),
Indexer.BuildTool(
"scala-cli",
scalaCli.buildTargetsData,
scalaCli.lastImportedBuild,
),
),
)
++
scalaCli.lastImportedBuilds.map {
case (lastImportedBuild, buildTargetsData) =>
Indexer
.BuildTool("scala-cli", buildTargetsData, lastImportedBuild)
},
clientConfig,
definitionIndex,
() => referencesProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ class WorkspaceFolders(

private val folderServices: AtomicReference[WorkspaceFoldersServices] = {
val (scalaProjects, nonScalaProjects) =
initialFolders.partition(_.isMetalsProject) match {
case (Nil, nonScala) => (List(nonScala.head), nonScala.tail)
case t => t
}
initialFolders.partition(_.isMetalsProject)
val services = scalaProjects.map(createService(_))
new AtomicReference(WorkspaceFoldersServices(services, nonScalaProjects))
}
Expand Down
Loading

0 comments on commit ff9d08a

Please sign in to comment.