Skip to content

Commit 448dc18

Browse files
committed
Pass scalafmt configuration from command line.
1 parent 88cb353 commit 448dc18

File tree

7 files changed

+68
-31
lines changed

7 files changed

+68
-31
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ final case class FmtOptions(
5050
@Name("scalafmtConfig")
5151
scalafmtConf: Option[String] = None,
5252
@Group("Format")
53+
@HelpMessage("Pass configuration as a string.")
54+
@Name("scalafmtConfigStr")
55+
@Name("scalafmtConfSnippet")
56+
scalafmtConfStr: Option[String] = None,
57+
@Group("Format")
5358
@HelpMessage("Pass a global dialect for scalafmt. This overrides whatever value is configured in the .scalafmt.conf file.")
5459
@Name("dialect")
5560
scalafmtDialect: Option[String] = None,

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ object Fmt extends ScalaCommand[FmtOptions] {
3737
(s, i.workspace, Some(i))
3838
}
3939
CurrentParams.workspaceOpt = Some(workspace)
40-
val (versionMaybe, dialectMaybe, pathMaybe) =
41-
readVersionAndDialectFromFile(workspace, options.scalafmtConf, logger)
42-
val cache = options.shared.buildOptions().archiveCache
43-
val buildOptions = options.buildOptions
40+
val (versionMaybe, dialectMaybe, pathMaybe) = readVersionAndDialect(workspace, options, logger)
41+
val cache = options.shared.buildOptions().archiveCache
42+
val buildOptions = options.buildOptions
4443

4544
if (sourceFiles.isEmpty)
4645
logger.debug("No source files, not formatting anything")

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

Lines changed: 25 additions & 18 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,9 +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,
38-
customConfPath: Option[String],
40+
options: FmtOptions,
3941
logger: Logger
4042
): (Option[String], Option[String], Option[os.Path]) = {
4143
case class RunnerMetaconfig(dialect: String = "")
@@ -57,25 +59,30 @@ object FmtUtil {
5759
implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtMetaconfig] =
5860
metaconfig.generic.deriveDecoder[ScalafmtMetaconfig](default)
5961
}
60-
6162
val confName = ".scalafmt.conf"
6263
val pathMaybe =
63-
customConfPath.flatMap { p =>
64-
val confPath = os.Path(p, os.pwd)
65-
logger.debug(s"Checking for $confPath.")
66-
if (os.exists(confPath)) Some(confPath)
67-
else
68-
logger.message(s"WARNING: provided file doesn't exist $confPath")
69-
None
64+
options.scalafmtConfStr.flatMap { s =>
65+
val tmpConfPath = workspace / Constants.workspaceDirName / ".scalafmt.conf"
66+
os.write.over(tmpConfPath, s, createFolders = true)
67+
Some(tmpConfPath)
7068
}.orElse {
71-
logger.debug(s"Checking for $confName in cwd.")
72-
val confInCwd = workspace / confName
73-
if (os.exists(confInCwd)) Some(confInCwd)
74-
else {
75-
logger.debug(s"Checking for $confName in git root.")
76-
val gitRootMaybe = getGitRoot(workspace, logger)
77-
val confInGitRootMaybe = gitRootMaybe.map(os.Path(_) / confName)
78-
confInGitRootMaybe.find(os.exists(_))
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+
}
7986
}
8087
}
8188

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, None, 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, None, 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: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,11 @@ class FmtTests extends ScalaCliSuite {
5555
)
5656

5757
val simpleInputsWithCustomConfLocation: TestInputs = TestInputs(
58-
Seq(
59-
os.rel / "custom.conf" ->
60-
s"""|version = "3.5.5"
61-
|runner.dialect = scala213
62-
|""".stripMargin,
63-
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
64-
)
58+
os.rel / "custom.conf" ->
59+
s"""|version = "3.5.5"
60+
|runner.dialect = scala213
61+
|""".stripMargin,
62+
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
6563
)
6664

6765
private def noCrLf(input: String): String =
@@ -162,6 +160,21 @@ class FmtTests extends ScalaCliSuite {
162160
}
163161
}
164162

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+
165178
test("creating workspace conf file") {
166179
simpleInputsWithDialectOnly.fromRoot { root =>
167180
val workspaceConfPath = root / Constants.workspaceDirName / confFileName

website/docs/commands/fmt.md

Lines changed: 6 additions & 0 deletions
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
@@ -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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,12 @@ Aliases: `--scalafmt-config`
528528

529529
Custom path to the scalafmt configuration file.
530530

531+
#### `--scalafmt-conf-str`
532+
533+
Aliases: `--scalafmt-config-str`, `--scalafmt-conf-snippet`
534+
535+
Pass configuration as a string.
536+
531537
#### `--scalafmt-dialect`
532538

533539
Aliases: `--dialect`

0 commit comments

Comments
 (0)