Skip to content

Commit

Permalink
ScalafmtRunner: use Future, not .par
Browse files Browse the repository at this point in the history
  • Loading branch information
kitbellew committed Jan 27, 2025
1 parent edd06ea commit a9175bb
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package org.scalafmt.cli

import org.scalafmt.CompatCollections.ParConverters._
import org.scalafmt.Error
import org.scalafmt.dynamic.ScalafmtDynamicError
import org.scalafmt.interfaces.Scalafmt
import org.scalafmt.interfaces.ScalafmtSession
import org.scalafmt.sysops.FileOps

import java.nio.file.Path
import java.util.concurrent.atomic.AtomicReference

import scala.util.control.Breaks._
import scala.concurrent.Future

object ScalafmtDynamicRunner extends ScalafmtRunner {
override private[cli] def run(
Expand All @@ -34,28 +32,13 @@ object ScalafmtDynamicRunner extends ScalafmtRunner {
x => customMatcher(x) && sessionMatcher(x)
}
val inputMethods = getInputMethods(options, filterMatcher)

val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage)

val exitCode = new AtomicReference(ExitCode.Ok)
breakable(inputMethods.compatPar.foreach { inputMethod =>
try {
val code = handleFile(inputMethod, session, options)
exitCode.getAndUpdate(ExitCode.merge(code, _))
} catch {
case e: Error.MisformattedFile =>
reporter.error(e.file, e)
if (options.check) break
}
termDisplay.taskProgress(termDisplayMessage)
})

val exit = ExitCode.merge(exitCode.get, reporter.getExitCode)

termDisplay.completedTask(termDisplayMessage, exit.isOk)
termDisplay.stop()

exit
if (inputMethods.isEmpty) ExitCode.Ok
else runInputs(options, inputMethods, termDisplayMessage) { inputMethod =>
import org.scalafmt.sysops.PlatformCompat.executionContext
Future(handleFile(inputMethod, session, options)).recover {
case x: Error.MisformattedFile => reporter.fail(x)(x.file)
}.map(ExitCode.merge(_, reporter.getExitCode))
}
}

private[this] def handleFile(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.scalafmt.cli

import org.scalafmt.CompatCollections.ParConverters._
import org.scalafmt.Error
import org.scalafmt.Formatted
import org.scalafmt.Scalafmt
Expand All @@ -12,11 +11,8 @@ import org.scalafmt.config.ScalafmtConfigException
import scala.meta.parsers.ParseException
import scala.meta.tokenizers.TokenizeException

import java.util.concurrent.atomic.AtomicReference

import scala.annotation.tailrec

import util.control.Breaks
import scala.concurrent.Future

object ScalafmtCoreRunner extends ScalafmtRunner {
override private[cli] def run(
Expand All @@ -31,41 +27,29 @@ object ScalafmtCoreRunner extends ScalafmtRunner {
try ProjectFiles.FileMatcher(scalafmtConf.project, options.customExcludes)
catch {
case e: ScalafmtConfigException =>
options.common.err.println(e.getMessage())
options.common.err.println(e.getMessage)
return ExitCode.UnexpectedError // RETURNING EARLY!
}

val inputMethods = getInputMethods(options, filterMatcher.matchesPath)
val adjustedScalafmtConf = {
if (scalafmtConf.needGitAutoCRLF) options.gitOps.getAutoCRLF else None
}.fold(scalafmtConf)(scalafmtConf.withGitAutoCRLF)
if (inputMethods.isEmpty) ExitCode.Ok
else {
val adjustedScalafmtConf = {
if (scalafmtConf.needGitAutoCRLF) options.gitOps.getAutoCRLF else None
}.fold(scalafmtConf)(scalafmtConf.withGitAutoCRLF)

val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage)
val exitCode = new AtomicReference(ExitCode.Ok)
Breaks.breakable(inputMethods.compatPar.foreach { inputMethod =>
val code = handleFile(inputMethod, options, adjustedScalafmtConf)
exitCode.getAndUpdate(ExitCode.merge(code, _))
if (options.check && !code.isOk) Breaks.break
termDisplay.taskProgress(termDisplayMessage)
})
termDisplay.completedTask(termDisplayMessage, exitCode.get.isOk)
termDisplay.stop()
exitCode.get()
runInputs(options, inputMethods, termDisplayMessage) { inputMethod =>
import org.scalafmt.sysops.PlatformCompat.executionContext
Future(handleFile(inputMethod, options, adjustedScalafmtConf)).recover {
case e: Error.MisformattedFile =>
options.common.err.println(e.customMessage)
ExitCode.TestError
}
}
}
}

private[this] def handleFile(
inputMethod: InputMethod,
options: CliOptions,
config: ScalafmtConfig,
): ExitCode =
try unsafeHandleFile(inputMethod, options, config)
catch {
case Error.MisformattedFile(_, diff) =>
options.common.err.println(diff)
ExitCode.TestError
}

private[this] def unsafeHandleFile(
inputMethod: InputMethod,
options: CliOptions,
scalafmtConfig: ScalafmtConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ package org.scalafmt.cli
import org.scalafmt.Error
import org.scalafmt.sysops.AbsoluteFile
import org.scalafmt.sysops.BatchPathFinder
import org.scalafmt.sysops.PlatformCompat

import java.nio.file.Path

import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.Promise
import scala.concurrent.duration

trait ScalafmtRunner {
private[cli] def run(
options: CliOptions,
Expand Down Expand Up @@ -68,4 +75,40 @@ trait ScalafmtRunner {
val excludeRegexp = options.excludeFilterRegexp.pattern
files.filter(f => !excludeRegexp.matcher(f.toString()).find())
}

protected def runInputs(
options: CliOptions,
inputMethods: Seq[InputMethod],
termDisplayMessage: String,
)(f: InputMethod => Future[ExitCode]): ExitCode = {
val termDisplay = newTermDisplay(options, inputMethods, termDisplayMessage)

implicit val executionContext: ExecutionContext =
PlatformCompat.executionContext
val completed = Promise[ExitCode]()

val tasks = List.newBuilder[Future[ExitCode]]
inputMethods.foreach(inputMethod =>
if (!completed.isCompleted) {
val future = f(inputMethod)
future.onComplete(r =>
if (options.check && !r.toOption.exists(_.isOk)) completed
.tryComplete(r)
else termDisplay.taskProgress(termDisplayMessage),
)
tasks += future
},
)

Future.foldLeft(tasks.result())(ExitCode.Ok)(ExitCode.merge)
.onComplete(completed.tryComplete)

completed.future.onComplete { r =>
termDisplay.completedTask(termDisplayMessage, r.toOption.exists(_.isOk))
termDisplay.stop()
}

Await.result(completed.future, duration.Duration.Inf)
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package org.scalafmt.sysops

import scala.concurrent.ExecutionContext

private[scalafmt] object PlatformCompat {
def isScalaNative = false
def prepareCommand(cmd: Seq[String]) = cmd
def fixPathOnNativeWindows(path: String) = path
def isNativeOnWindows() = false
def relativize(base: AbsoluteFile, path: AbsoluteFile) = base.toUri
.relativize(path.toUri)

implicit def executionContext: ExecutionContext = ExecutionContext.global

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.scalafmt.sysops

import scala.concurrent.ExecutionContext
import scala.scalanative.runtime.Platform

private[scalafmt] object PlatformCompat {
Expand All @@ -12,4 +13,7 @@ private[scalafmt] object PlatformCompat {
def relativize(base: AbsoluteFile, file: AbsoluteFile) =
if (Platform.isWindows()) base.path.relativize(file.path)
else base.toUri.relativize(file.toUri)

implicit def executionContext: ExecutionContext = ExecutionContext.global

}

0 comments on commit a9175bb

Please sign in to comment.