Skip to content

Commit 17efc58

Browse files
authored
Merge pull request #1227 from wleczny/scalafmt-conf-custom-location
Add `--scalafmt-conf` and `scalafmt-conf-str` options for `fmt` command.
2 parents 0a926c0 + 448dc18 commit 17efc58

File tree

7 files changed

+96
-16
lines changed

7 files changed

+96
-16
lines changed

modules/cli-options/src/main/scala/scala/cli/commands/FmtOptions.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ final case class FmtOptions(
4646
scalafmtArg: List[String] = Nil,
4747

4848
@Group("Format")
49+
@HelpMessage("Custom path to the scalafmt configuration file.")
50+
@Name("scalafmtConfig")
51+
scalafmtConf: Option[String] = None,
52+
@Group("Format")
53+
@HelpMessage("Pass configuration as a string.")
54+
@Name("scalafmtConfigStr")
55+
@Name("scalafmtConfSnippet")
56+
scalafmtConfStr: Option[String] = None,
57+
@Group("Format")
4958
@HelpMessage("Pass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file.")
5059
@Name("dialect")
5160
scalafmtDialect: Option[String] = None,

modules/cli/src/main/scala/scala/cli/commands/Fmt.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object Fmt extends ScalaCommand[FmtOptions] {
3737
(s, i.workspace, Some(i))
3838
}
3939
CurrentParams.workspaceOpt = Some(workspace)
40-
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialectFromFile(workspace, logger)
40+
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialect(workspace, options, logger)
4141
val cache = options.shared.buildOptions().archiveCache
4242
val buildOptions = options.buildOptions
4343

modules/cli/src/main/scala/scala/cli/commands/util/FmtUtil.scala

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import com.typesafe.config.ConfigSyntax
55
import com.typesafe.config.parser.ConfigDocument
66
import com.typesafe.config.parser.ConfigDocumentFactory
77
import scala.build.Logger
8+
import scala.build.internal.Constants
89
import scala.util.control.NonFatal
10+
import scala.cli.commands.FmtOptions
911

1012
object FmtUtil {
1113
private def getGitRoot(workspace: os.Path, logger: Logger): Option[String] =
@@ -33,8 +35,9 @@ object FmtUtil {
3335
* @return
3436
* path to found `.scalafmt.conf` file and `version` with `dialect` read from it
3537
*/
36-
def readVersionAndDialectFromFile(
38+
def readVersionAndDialect(
3739
workspace: os.Path,
40+
options: FmtOptions,
3841
logger: Logger
3942
): (Option[String], Option[String], Option[os.Path]) = {
4043
case class RunnerMetaconfig(dialect: String = "")
@@ -56,19 +59,32 @@ object FmtUtil {
5659
implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtMetaconfig] =
5760
metaconfig.generic.deriveDecoder[ScalafmtMetaconfig](default)
5861
}
59-
6062
val confName = ".scalafmt.conf"
61-
val pathMaybe = {
62-
logger.debug(s"Checking for $confName in cwd.")
63-
val confInCwd = workspace / confName
64-
if (os.exists(confInCwd)) Some(confInCwd)
65-
else {
66-
logger.debug(s"Checking for $confName in git root.")
67-
val gitRootMaybe = getGitRoot(workspace, logger)
68-
val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)
69-
confInGitRootMaybe.find(os.exists(_))
63+
val pathMaybe =
64+
options.scalafmtConfStr.flatMap { s =>
65+
val tmpConfPath = workspace / Constants.workspaceDirName / ".scalafmt.conf"
66+
os.write.over(tmpConfPath, s, createFolders = true)
67+
Some(tmpConfPath)
68+
}.orElse {
69+
options.scalafmtConf.flatMap { p =>
70+
val confPath = os.Path(p, os.pwd)
71+
logger.debug(s"Checking for $confPath.")
72+
if (os.exists(confPath)) Some(confPath)
73+
else
74+
logger.message(s"WARNING: provided file doesn't exist $confPath")
75+
None
76+
}.orElse {
77+
logger.debug(s"Checking for $confName in cwd.")
78+
val confInCwd = workspace / confName
79+
if (os.exists(confInCwd)) Some(confInCwd)
80+
else {
81+
logger.debug(s"Checking for $confName in git root.")
82+
val gitRootMaybe = getGitRoot(workspace, logger)
83+
val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)
84+
confInGitRootMaybe.find(os.exists(_))
85+
}
86+
}
7087
}
71-
}
7288

7389
val confContentMaybe = pathMaybe.flatMap { path =>
7490
val either = metaconfig.Hocon.parseInput[ScalafmtMetaconfig](

modules/cli/src/test/scala/cli/tests/ScalafmtTest.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import com.eed3si9n.expecty.Expecty.expect
33

44
import scala.build.tests.{TestInputs, TestLogger}
55
import scala.cli.commands.util.FmtUtil
6+
import scala.cli.commands.FmtOptions
67

78
class ScalafmtTests extends munit.FunSuite {
89

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

1819
val readVersionAndDialect =
19-
FmtUtil.readVersionAndDialectFromFile(workspace = dirPath, TestLogger())
20+
FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())
2021
expect(readVersionAndDialect == (Some("3.1.2"), Some("scala213"), Some(confFilePath)))
2122
}
2223
}
2324

2425
test("readVersionFromFile with missing .scalafmt.conf file") {
2526
TestInputs.withTmpDir("temp-dir") { dirPath =>
2627
val readVersionAndDialect =
27-
FmtUtil.readVersionAndDialectFromFile(workspace = dirPath, TestLogger())
28+
FmtUtil.readVersionAndDialect(workspace = dirPath, FmtOptions(), TestLogger())
2829
expect(readVersionAndDialect == (None, None, None))
2930
}
3031
}

modules/integration/src/test/scala/scala/cli/integration/FmtTests.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ class FmtTests extends ScalaCliSuite {
5454
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
5555
)
5656

57+
val simpleInputsWithCustomConfLocation: TestInputs = TestInputs(
58+
os.rel / "custom.conf" ->
59+
s"""|version = "3.5.5"
60+
|runner.dialect = scala213
61+
|""".stripMargin,
62+
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
63+
)
64+
5765
private def noCrLf(input: String): String =
5866
input.replaceAll("\r\n", "\n")
5967

@@ -139,6 +147,34 @@ class FmtTests extends ScalaCliSuite {
139147
}
140148
}
141149

150+
test("--scalafmt-conf") {
151+
simpleInputsWithCustomConfLocation.fromRoot { root =>
152+
os.proc(TestUtil.cli, "fmt", ".", "--scalafmt-conf", "custom.conf").call(cwd = root)
153+
val confLines = os.read.lines(root / Constants.workspaceDirName / confFileName)
154+
val versionInConf = confLines(0).stripPrefix("version = ").trim
155+
val dialectInConf = confLines(1).stripPrefix("runner.dialect = ").trim
156+
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
157+
expect(versionInConf == "\"3.5.5\"")
158+
expect(dialectInConf == "scala213")
159+
expect(updatedContent == expectedSimpleInputsFormattedContent)
160+
}
161+
}
162+
163+
test("--scalafmt-conf-str") {
164+
simpleInputsWithVersionOnly.fromRoot { root =>
165+
val confStr =
166+
s"""version = 3.5.7${System.lineSeparator}runner.dialect = scala213${System.lineSeparator}"""
167+
os.proc(TestUtil.cli, "fmt", ".", "--scalafmt-conf-str", s"$confStr").call(cwd = root)
168+
val confLines = os.read.lines(root / Constants.workspaceDirName / confFileName)
169+
val versionInConf = confLines(0).stripPrefix("version = ")
170+
val dialectInConf = confLines(1).stripPrefix("runner.dialect = ")
171+
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
172+
expect(versionInConf == "\"3.5.7\"")
173+
expect(dialectInConf == "scala213")
174+
expect(updatedContent == expectedSimpleInputsFormattedContent)
175+
}
176+
}
177+
142178
test("creating workspace conf file") {
143179
simpleInputsWithDialectOnly.fromRoot { root =>
144180
val workspaceConfPath = root / Constants.workspaceDirName / confFileName

website/docs/commands/fmt.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ You can skip passing either of those, which will make Scala CLI to infer a defau
3030
- 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.
3131
- 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).
3232

33+
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.
34+
3335
#### Example 1
3436

3537
``` text title=.scalafmt.conf
@@ -153,7 +155,7 @@ scala-cli fmt . --check --respect-project-filters=false
153155

154156
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:
155157

156-
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:
158+
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:
157159

158160
1. Configuration file with the specified version and dialect is found.
159161
2. Configuration file is found, but it doesn't have specified version or dialect.
@@ -167,3 +169,7 @@ If the `--save-scalafmt-conf` option is passed, then `fmt` command behaves as fo
167169
- In the **first** case `fmt` uses the found `.scalafmt.conf` file to run `scalafmt`.
168170
- 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`.
169171
- 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`.
172+
173+
:::note
174+
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.
175+
:::

website/docs/reference/cli-options.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,18 @@ Show help for scalafmt. This is an alias for --scalafmt-arg -help
522522

523523
Aliases: `-F`
524524

525+
#### `--scalafmt-conf`
526+
527+
Aliases: `--scalafmt-config`
528+
529+
Custom path to the scalafmt configuration file.
530+
531+
#### `--scalafmt-conf-str`
532+
533+
Aliases: `--scalafmt-config-str`, `--scalafmt-conf-snippet`
534+
535+
Pass configuration as a string.
536+
525537
#### `--scalafmt-dialect`
526538

527539
Aliases: `--dialect`

0 commit comments

Comments
 (0)