Skip to content

Commit

Permalink
Merge pull request #1227 from wleczny/scalafmt-conf-custom-location
Browse files Browse the repository at this point in the history
Add `--scalafmt-conf` and `scalafmt-conf-str` options for `fmt` command.
  • Loading branch information
wleczny committed Aug 4, 2022
2 parents 0a926c0 + 448dc18 commit 17efc58
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ final case class FmtOptions(
scalafmtArg: List[String] = Nil,

@Group("Format")
@HelpMessage("Custom path to the scalafmt configuration file.")
@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
2 changes: 1 addition & 1 deletion modules/cli/src/main/scala/scala/cli/commands/Fmt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object Fmt extends ScalaCommand[FmtOptions] {
(s, i.workspace, Some(i))
}
CurrentParams.workspaceOpt = Some(workspace)
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialectFromFile(workspace, logger)
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialect(workspace, options, logger)
val cache = options.shared.buildOptions().archiveCache
val buildOptions = options.buildOptions

Expand Down
40 changes: 28 additions & 12 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,8 +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,
options: FmtOptions,
logger: Logger
): (Option[String], Option[String], Option[os.Path]) = {
case class RunnerMetaconfig(dialect: String = "")
Expand All @@ -56,19 +59,32 @@ object FmtUtil {
implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtMetaconfig] =
metaconfig.generic.deriveDecoder[ScalafmtMetaconfig](default)
}

val confName = ".scalafmt.conf"
val pathMaybe = {
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(_))
val pathMaybe =
options.scalafmtConfStr.flatMap { s =>
val tmpConfPath = workspace / Constants.workspaceDirName / ".scalafmt.conf"
os.write.over(tmpConfPath, s, createFolders = true)
Some(tmpConfPath)
}.orElse {
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(_))
}
}
}
}

val confContentMaybe = pathMaybe.flatMap { path =>
val either = metaconfig.Hocon.parseInput[ScalafmtMetaconfig](
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, 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, 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 @@ -54,6 +54,14 @@ class FmtTests extends ScalaCliSuite {
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
)

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

private def noCrLf(input: String): String =
input.replaceAll("\r\n", "\n")

Expand Down Expand Up @@ -139,6 +147,34 @@ class FmtTests extends ScalaCliSuite {
}
}

test("--scalafmt-conf") {
simpleInputsWithCustomConfLocation.fromRoot { root =>
os.proc(TestUtil.cli, "fmt", ".", "--scalafmt-conf", "custom.conf").call(cwd = root)
val confLines = os.read.lines(root / Constants.workspaceDirName / confFileName)
val versionInConf = confLines(0).stripPrefix("version = ").trim
val dialectInConf = confLines(1).stripPrefix("runner.dialect = ").trim
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
expect(versionInConf == "\"3.5.5\"")
expect(dialectInConf == "scala213")
expect(updatedContent == expectedSimpleInputsFormattedContent)
}
}

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
8 changes: 7 additions & 1 deletion 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 @@ -153,7 +155,7 @@ scala-cli fmt . --check --respect-project-filters=false

The Scala CLI `fmt` command runs `scalafmt` under the hood, which *normally* requires `.scalafmt.conf` configuration file with explicitly specified **version** and **dialect** fields. The way it is handled by Scala CLI is as follows:

At the beginning `fmt` looks for existing `.scalafmt.conf` file inside **current workspace** directory and if it doesn't find it - inside **git root** directory. There are 3 possible cases:
At the beginning `fmt` looks for the configuration inside the file specified in the `--scalafmt-conf` option. If the option is not passed or the file doesn't exist, `fmt` looks for the existing configuration file inside **current workspace** directory. If the file is still not found, `fmt` looks for it inside **git root** directory. There are 3 possible cases:

1. Configuration file with the specified version and dialect is found.
2. Configuration file is found, but it doesn't have specified version or dialect.
Expand All @@ -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.
:::
12 changes: 12 additions & 0 deletions website/docs/reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,18 @@ Show help for scalafmt. This is an alias for --scalafmt-arg -help

Aliases: `-F`

#### `--scalafmt-conf`

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 17efc58

Please sign in to comment.