Skip to content

Commit

Permalink
Pass scalafmt configuration from command line.
Browse files Browse the repository at this point in the history
  • Loading branch information
wleczny committed Aug 3, 2022
1 parent 88cb353 commit 448dc18
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ final case class FmtOptions(
@Name("scalafmtConfig")
scalafmtConf: Option[String] = None,
@Group("Format")
@HelpMessage("Pass configuration as a string.")
@Name("scalafmtConfigStr")
@Name("scalafmtConfSnippet")
scalafmtConfStr: Option[String] = None,
@Group("Format")
@HelpMessage("Pass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file.")
@Name("dialect")
scalafmtDialect: Option[String] = None,
Expand Down
7 changes: 3 additions & 4 deletions modules/cli/src/main/scala/scala/cli/commands/Fmt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ object Fmt extends ScalaCommand[FmtOptions] {
(s, i.workspace, Some(i))
}
CurrentParams.workspaceOpt = Some(workspace)
val (versionMaybe, dialectMaybe, pathMaybe) =
readVersionAndDialectFromFile(workspace, options.scalafmtConf, logger)
val cache = options.shared.buildOptions().archiveCache
val buildOptions = options.buildOptions
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialect(workspace, options, logger)
val cache = options.shared.buildOptions().archiveCache
val buildOptions = options.buildOptions

if (sourceFiles.isEmpty)
logger.debug("No source files, not formatting anything")
Expand Down
43 changes: 25 additions & 18 deletions modules/cli/src/main/scala/scala/cli/commands/util/FmtUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import com.typesafe.config.ConfigSyntax
import com.typesafe.config.parser.ConfigDocument
import com.typesafe.config.parser.ConfigDocumentFactory
import scala.build.Logger
import scala.build.internal.Constants
import scala.util.control.NonFatal
import scala.cli.commands.FmtOptions

object FmtUtil {
private def getGitRoot(workspace: os.Path, logger: Logger): Option[String] =
Expand Down Expand Up @@ -33,9 +35,9 @@ object FmtUtil {
* @return
* path to found `.scalafmt.conf` file and `version` with `dialect` read from it
*/
def readVersionAndDialectFromFile(
def readVersionAndDialect(
workspace: os.Path,
customConfPath: Option[String],
options: FmtOptions,
logger: Logger
): (Option[String], Option[String], Option[os.Path]) = {
case class RunnerMetaconfig(dialect: String = "")
Expand All @@ -57,25 +59,30 @@ object FmtUtil {
implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtMetaconfig] =
metaconfig.generic.deriveDecoder[ScalafmtMetaconfig](default)
}

val confName = ".scalafmt.conf"
val pathMaybe =
customConfPath.flatMap { p =>
val confPath = os.Path(p, os.pwd)
logger.debug(s"Checking for $confPath.")
if (os.exists(confPath)) Some(confPath)
else
logger.message(s"WARNING: provided file doesn't exist $confPath")
None
options.scalafmtConfStr.flatMap { s =>
val tmpConfPath = workspace / Constants.workspaceDirName / ".scalafmt.conf"
os.write.over(tmpConfPath, s, createFolders = true)
Some(tmpConfPath)
}.orElse {
logger.debug(s"Checking for $confName in cwd.")
val confInCwd = workspace / confName
if (os.exists(confInCwd)) Some(confInCwd)
else {
logger.debug(s"Checking for $confName in git root.")
val gitRootMaybe = getGitRoot(workspace, logger)
val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)
confInGitRootMaybe.find(os.exists(_))
options.scalafmtConf.flatMap { p =>
val confPath = os.Path(p, os.pwd)
logger.debug(s"Checking for $confPath.")
if (os.exists(confPath)) Some(confPath)
else
logger.message(s"WARNING: provided file doesn't exist $confPath")
None
}.orElse {
logger.debug(s"Checking for $confName in cwd.")
val confInCwd = workspace / confName
if (os.exists(confInCwd)) Some(confInCwd)
else {
logger.debug(s"Checking for $confName in git root.")
val gitRootMaybe = getGitRoot(workspace, logger)
val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)
confInGitRootMaybe.find(os.exists(_))
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions modules/cli/src/test/scala/cli/tests/ScalafmtTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import com.eed3si9n.expecty.Expecty.expect

import scala.build.tests.{TestInputs, TestLogger}
import scala.cli.commands.util.FmtUtil
import scala.cli.commands.FmtOptions

class ScalafmtTests extends munit.FunSuite {

Expand All @@ -16,15 +17,15 @@ class ScalafmtTests extends munit.FunSuite {
os.write(confFilePath, confFile)

val readVersionAndDialect =
FmtUtil.readVersionAndDialectFromFile(workspace = dirPath, None, TestLogger())
FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())
expect(readVersionAndDialect == (Some("3.1.2"), Some("scala213"), Some(confFilePath)))
}
}

test("readVersionFromFile with missing .scalafmt.conf file") {
TestInputs.withTmpDir("temp-dir") { dirPath =>
val readVersionAndDialect =
FmtUtil.readVersionAndDialectFromFile(workspace = dirPath, None, TestLogger())
FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())
expect(readVersionAndDialect == (None, None, None))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,11 @@ class FmtTests extends ScalaCliSuite {
)

val simpleInputsWithCustomConfLocation: TestInputs = TestInputs(
Seq(
os.rel / "custom.conf" ->
s"""|version = "3.5.5"
|runner.dialect = scala213
|""".stripMargin,
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
)
os.rel / "custom.conf" ->
s"""|version = "3.5.5"
|runner.dialect = scala213
|""".stripMargin,
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
)

private def noCrLf(input: String): String =
Expand Down Expand Up @@ -162,6 +160,21 @@ class FmtTests extends ScalaCliSuite {
}
}

test("--scalafmt-conf-str") {
simpleInputsWithVersionOnly.fromRoot { root =>
val confStr =
s"""version = 3.5.7${System.lineSeparator}runner.dialect = scala213${System.lineSeparator}"""
os.proc(TestUtil.cli, "fmt", ".", "--scalafmt-conf-str", s"$confStr").call(cwd = root)
val confLines = os.read.lines(root / Constants.workspaceDirName / confFileName)
val versionInConf = confLines(0).stripPrefix("version = ")
val dialectInConf = confLines(1).stripPrefix("runner.dialect = ")
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
expect(versionInConf == "\"3.5.7\"")
expect(dialectInConf == "scala213")
expect(updatedContent == expectedSimpleInputsFormattedContent)
}
}

test("creating workspace conf file") {
simpleInputsWithDialectOnly.fromRoot { root =>
val workspaceConfPath = root / Constants.workspaceDirName / confFileName
Expand Down
6 changes: 6 additions & 0 deletions website/docs/commands/fmt.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ You can skip passing either of those, which will make Scala CLI to infer a defau
- If a `.scalafmt.conf` file is present in the workspace and it has the field defined, the value will be read from there, unless explicitly specified with Scala CLI options.
- Otherwise, the default `scalafmt` **version** will be the latest one used by your Scala CLI version (so it is subject to change when updating Scala CLI). The default **dialect** will be inferred based on Scala version (defined explicitly by `-S` option, or default version if option would not be passed).

It is possible to pass the configuration as a string directly from the command line, using `--scalafmt-conf-str` option. If the configuration is passed this way, Scala CLI will behave exactly the same as if it would find the specified configuration in a `.scalafmt.conf` file in the workspace.

#### Example 1

``` text title=.scalafmt.conf
Expand Down Expand Up @@ -167,3 +169,7 @@ If the `--save-scalafmt-conf` option is passed, then `fmt` command behaves as fo
- In the **first** case `fmt` uses the found `.scalafmt.conf` file to run `scalafmt`.
- In the **second** case `fmt` [infers](/docs/commands/fmt#scalafmt-version-and-dialect) missing parameters, writes them directly into the previously found file and then uses this file to run `scalafmt`.
- In the **third** case `fmt` creates a `.scalafmt.conf` file in the current workspace directory, writes [inferred](/docs/commands/fmt#scalafmt-version-and-dialect) version and dialect into it and uses it to run `scalafmt`.

:::note
If the configuration is passed in the `--scalafmt-conf-str` option, Scala CLI will behave exactly the same as if it would find the specified configuration in a `.scalafmt.conf` file in the workspace.
:::
6 changes: 6 additions & 0 deletions website/docs/reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,12 @@ Aliases: `--scalafmt-config`

Custom path to the scalafmt configuration file.

#### `--scalafmt-conf-str`

Aliases: `--scalafmt-config-str`, `--scalafmt-conf-snippet`

Pass configuration as a string.

#### `--scalafmt-dialect`

Aliases: `--dialect`
Expand Down

0 comments on commit 448dc18

Please sign in to comment.