Skip to content
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

improvement: don't create service for folders w/ non-Scala projects #5562

Merged
merged 3 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,28 @@ object MetalsEnrichments

implicit class XtensionAbsolutePathBuffers(path: AbsolutePath) {

def hasScalaFiles: Boolean = {
def isScalaDir(file: File): Boolean = {
def isScalaProject(): Boolean =
containsProjectFilesSatisfying(_.isScala)
def isMetalsProject(): Boolean =
containsProjectFilesSatisfying(_.isScalaOrJavaFilename)

private def containsProjectFilesSatisfying(
fileNamePredicate: String => Boolean
): Boolean = {
val directoriesToCheck = Set("test", "src", "it")
kasiaMarek marked this conversation as resolved.
Show resolved Hide resolved
def dirFilter(f: File) = directoriesToCheck(f.getName()) || f
.listFiles()
.exists(dir => dir.isDirectory && directoriesToCheck(dir.getName()))
def isScalaDir(
file: File,
dirFilter: File => Boolean = _ => true,
): Boolean = {
file.listFiles().exists { file =>
if (file.isDirectory()) isScalaDir(file)
else file.getName().endsWith(".scala")
if (file.isDirectory()) dirFilter(file) && isScalaDir(file)
else fileNamePredicate(file.getName())
}
}
path.isDirectory && isScalaDir(path.toFile)
path.isDirectory && isScalaDir(path.toFile, dirFilter)
}

def scalaSourcerootOption: String = s""""-P:semanticdb:sourceroot:$path""""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package scala.meta.internal.metals

import java.net.URI
import java.nio.file._
import java.sql.Connection
import java.util
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ScheduledExecutorService
Expand Down Expand Up @@ -128,10 +127,11 @@ class MetalsLspService(
shellRunner: ShellRunner,
timerProvider: TimerProvider,
initTreeView: () => Unit,
val folder: AbsolutePath,
folder: AbsolutePath,
folderVisibleName: Option[String],
headDoctor: HeadDoctor,
) extends Cancelable
) extends Folder(folder, folderVisibleName, isKnownMetalsProject = true)
with Cancelable
with TextDocumentService {
import serverInputs._

Expand Down Expand Up @@ -781,7 +781,7 @@ class MetalsLspService(

val treeView =
new FolderTreeViewProvider(
Folder(folder, folderVisibleName),
new Folder(folder, folderVisibleName, true),
kasiaMarek marked this conversation as resolved.
Show resolved Hide resolved
buildTargets,
() => buildClient.ongoingCompilations(),
definitionIndex,
Expand Down Expand Up @@ -847,7 +847,7 @@ class MetalsLspService(
cancelable
}

def loadFingerPrints(): Unit = {
private def loadFingerPrints(): Unit = {
// load fingerprints from last execution
fingerprints.addAll(tables.fingerprints.load())
}
Expand All @@ -859,7 +859,7 @@ class MetalsLspService(
token: CancelToken,
): Future[Unit] = codeActionProvider.executeCommands(params, token)

def registerNiceToHaveFilePatterns(): Unit = {
private def registerNiceToHaveFilePatterns(): Unit = {
for {
params <- Option(initializeParams)
capabilities <- Option(params.getCapabilities)
Expand All @@ -885,9 +885,11 @@ class MetalsLspService(

val isInitialized = new AtomicBoolean(false)

def connectTables(): Connection = tables.connect()
def initialized(): Future[Unit] = {
loadFingerPrints()
registerNiceToHaveFilePatterns()
tables.connect()

def initialized(): Future[Unit] =
for {
_ <- maybeSetupScalaCli()
_ <-
Expand All @@ -902,6 +904,7 @@ class MetalsLspService(
)
)
} yield ()
}

def onShutdown(): Unit = {
tables.fingerprints.save(fingerprints.getAllFingerprints())
Expand Down Expand Up @@ -2049,7 +2052,7 @@ class MetalsLspService(
if (
!buildTools.isAutoConnectable()
&& buildTools.loadSupported.isEmpty
&& folder.hasScalaFiles
&& folder.isScalaProject()
) scalaCli.setupIDE(folder)
else Future.successful(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,56 +10,98 @@ import scala.meta.internal.metals.logging.MetalsLogger
class WorkspaceFolders(
initialFolders: List[Folder],
createService: Folder => MetalsLspService,
onInitialize: MetalsLspService => Future[Unit],
shoutdownMetals: () => Future[Unit],
shutdownMetals: () => Future[Unit],
redirectSystemOut: Boolean,
initialServerConfig: MetalsServerConfig,
)(implicit ec: ExecutionContext) {

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

def getFolderServices: List[MetalsLspService] = folderServices.get()
def getFolderServices: List[MetalsLspService] = folderServices.get().services
def nonScalaProjects: List[Folder] = folderServices.get().nonScalaFolders

def changeFolderServices(
toRemove: List[Folder],
toAdd: List[Folder],
): Future[Unit] = {
val actualToRemove =
toRemove.filterNot(folder => toAdd.exists(_.path == folder.path))
def shouldBeRemoved(service: MetalsLspService) =
actualToRemove.exists(_.path == service.folder)
def isIn(services: List[MetalsLspService], service: MetalsLspService) =
services.exists(_.folder == service.folder)

val newServices = toAdd.map { folder =>
val newService = createService(folder)
newService.loadFingerPrints()
newService.registerNiceToHaveFilePatterns()
newService.connectTables()
newService
}
def shouldBeRemoved(folder: Folder) =
actualToRemove.exists(_.path == folder.path)

val (newScala, newNonScala) = toAdd.partition(_.isMetalsProject)
val newServices = newScala.map(createService(_))
if (newServices.isEmpty && getFolderServices.forall(shouldBeRemoved)) {
shoutdownMetals()
shutdownMetals()
} else {
val prev =
folderServices.getAndUpdate { current =>
val afterRemove = current.filterNot(shouldBeRemoved)
val newToAdd = newServices.filterNot(isIn(current, _))
afterRemove ++ newToAdd
val WorkspaceFoldersServices(prev, _) =
folderServices.getAndUpdate {
case WorkspaceFoldersServices(services, nonScalaProjects) =>
val updatedServices =
services.filterNot(shouldBeRemoved) ++
newServices.filterNot(isIn(services, _))
val updatedNonScala =
nonScalaProjects.filterNot(shouldBeRemoved) ++
newNonScala.filterNot(isIn(nonScalaProjects ++ services, _))
WorkspaceFoldersServices(
updatedServices,
updatedNonScala,
)
}
MetalsLogger.setupLspLogger(
folderServices.get().map(_.folder),
redirectSystemOut,
initialServerConfig,
)

setupLogger()

for {
_ <- Future.sequence(
newServices.filterNot(isIn(prev, _)).map(onInitialize)
newServices.filterNot(isIn(prev, _)).map(_.initialized())
)
_ <- Future(prev.filter(shouldBeRemoved).foreach(_.onShutdown()))
} yield ()
}
}

def convertToScalaProject(folder: Folder): MetalsLspService = {
val newService = createService(folder)
val WorkspaceFoldersServices(prev, _) = folderServices.getAndUpdate {
case wfs @ WorkspaceFoldersServices(services, nonScalaProjects) =>
if (!isIn(services, folder)) {
WorkspaceFoldersServices(
services :+ newService,
nonScalaProjects.filterNot(_ == folder),
)
} else wfs
}

prev.find(_.path == folder.path) match {
case Some(service) => service
case None =>
setupLogger()
newService.initialized()
newService
}
}

private def setupLogger() =
MetalsLogger.setupLspLogger(
getFolderServices.map(_.path),
redirectSystemOut,
initialServerConfig,
)

private def isIn(services: List[Folder], service: Folder) =
services.exists(_.path == service.path)

}

case class WorkspaceFoldersServices(
services: List[MetalsLspService],
nonScalaFolders: List[Folder],
)
Loading
Loading