Skip to content

Commit 8d643ee

Browse files
authored
Merge pull request #1135 from Gedochao/improve-fmt
Tweak the `fmt` command
2 parents 09bb2a1 + ea5e13e commit 8d643ee

File tree

6 files changed

+200
-35
lines changed

6 files changed

+200
-35
lines changed

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,18 @@ final case class FmtOptions(
99
shared: SharedOptions = SharedOptions(),
1010

1111
@Group("Format")
12-
@HelpMessage("Check that sources are well formatted")
12+
@HelpMessage("Check if sources are well formatted")
1313
check: Boolean = false,
1414

15+
@Group("Format")
16+
@HelpMessage("Use project filters defined in the configuration (turned on by default)")
17+
respectProjectFilters: Boolean = true,
18+
19+
@Group("Format")
20+
@HelpMessage("Show help for scalafmt. This is an alias for --scalafmt-arg -help")
21+
@Name("fmtHelp")
22+
scalafmtHelp: Boolean = false,
23+
1524
@Group("Format")
1625
@Hidden
1726
osArchSuffix: Option[String] = None,
@@ -30,7 +39,6 @@ final case class FmtOptions(
3039

3140
@Group("Format")
3241
@Name("F")
33-
@Hidden
3442
scalafmtArg: List[String] = Nil,
3543

3644
@Group("Format")

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

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ import caseapp._
44

55
import scala.build.interactive.InteractiveFileOps
66
import scala.build.internal.{Constants, CustomCodeWrapper, FetchExternalBinary, Runner}
7-
import scala.build.options.BuildOptions
87
import scala.build.{CrossSources, Inputs, Logger, Sources}
98
import scala.cli.CurrentParams
9+
import scala.cli.commands.util.FmtOptionsUtil._
1010
import scala.cli.commands.util.SharedOptionsUtil._
1111
import scala.cli.commands.util.VerbosityOptionsUtil._
12-
import scala.util.Properties
1312
import scala.util.control.NonFatal
1413

1514
object Fmt extends ScalaCommand[FmtOptions] {
16-
override def group = "Main"
17-
override def sharedOptions(options: FmtOptions) = Some(options.shared)
18-
override def names = List(
15+
override def group: String = "Main"
16+
override def sharedOptions(options: FmtOptions): Option[SharedOptions] = Some(options.shared)
17+
override def names: List[List[String]] = List(
1918
List("fmt"),
2019
List("format"),
2120
List("scalafmt")
@@ -43,7 +42,7 @@ object Fmt extends ScalaCommand[FmtOptions] {
4342
def readVersionFromFile(workspace: os.Path, logger: Logger): (Option[String], Boolean) = {
4443
case class ScalafmtVersionConfig(version: String = "")
4544
object ScalafmtVersionConfig {
46-
lazy val default = ScalafmtVersionConfig()
45+
lazy val default: ScalafmtVersionConfig = ScalafmtVersionConfig()
4746
implicit lazy val surface: metaconfig.generic.Surface[ScalafmtVersionConfig] =
4847
metaconfig.generic.deriveSurface[ScalafmtVersionConfig]
4948
implicit lazy val decoder: metaconfig.ConfDecoder[ScalafmtVersionConfig] =
@@ -77,26 +76,6 @@ object Fmt extends ScalaCommand[FmtOptions] {
7776
(versionMaybe, pathMaybe.isDefined)
7877
}
7978

80-
private implicit class FmtOptionsOps(v: FmtOptions) {
81-
import v._
82-
def binaryUrl(version: String): (String, Boolean) = {
83-
val osArchSuffix0 = osArchSuffix.map(_.trim).filter(_.nonEmpty)
84-
.getOrElse(FetchExternalBinary.platformSuffix())
85-
val tag0 = scalafmtTag.getOrElse("v" + version)
86-
val gitHubOrgName0 = scalafmtGithubOrgName.getOrElse("alexarchambault/scalafmt-native-image")
87-
val extension0 = if (Properties.isWin) ".zip" else ".gz"
88-
val url =
89-
s"https://github.com/$gitHubOrgName0/releases/download/$tag0/scalafmt-$osArchSuffix0$extension0"
90-
(url, !tag0.startsWith("v"))
91-
}
92-
93-
def buildOptions: BuildOptions =
94-
shared.buildOptions(ignoreErrors = false)
95-
96-
def scalafmtCliOptions: List[String] =
97-
scalafmtArg ::: (if (check) List("--check") else Nil)
98-
}
99-
10079
def run(options: FmtOptions, args: RemainingArgs): Unit = {
10180
CurrentParams.verbosity = options.shared.logging.verbosity
10281
val interactive = options.shared.logging.verbosityOptions.interactiveInstance()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package scala.cli.commands.util
2+
import scala.build.internal.FetchExternalBinary
3+
import scala.build.options.BuildOptions
4+
import scala.cli.commands.FmtOptions
5+
import scala.cli.commands.util.SharedOptionsUtil._
6+
import scala.util.Properties
7+
8+
object FmtOptionsUtil {
9+
implicit class FmtOptionsOps(v: FmtOptions) {
10+
import v._
11+
def binaryUrl(version: String): (String, Boolean) = {
12+
val osArchSuffix0 = osArchSuffix.map(_.trim).filter(_.nonEmpty)
13+
.getOrElse(FetchExternalBinary.platformSuffix())
14+
val tag0 = scalafmtTag.getOrElse("v" + version)
15+
val gitHubOrgName0 = scalafmtGithubOrgName.getOrElse("alexarchambault/scalafmt-native-image")
16+
val extension0 = if (Properties.isWin) ".zip" else ".gz"
17+
val url =
18+
s"https://github.com/$gitHubOrgName0/releases/download/$tag0/scalafmt-$osArchSuffix0$extension0"
19+
(url, !tag0.startsWith("v"))
20+
}
21+
22+
def buildOptions: BuildOptions = shared.buildOptions()
23+
24+
def scalafmtCliOptions: List[String] =
25+
scalafmtArg :::
26+
(if (check && !scalafmtArg.contains("--check")) List("--check") else Nil) :::
27+
(if (scalafmtHelp && !scalafmtArg.exists(Set("-h", "-help", "--help"))) List("--help")
28+
else Nil) :::
29+
(if (respectProjectFilters && !scalafmtArg.contains("--respect-project-filters"))
30+
List("--respect-project-filters")
31+
else Nil)
32+
}
33+
}

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

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import com.eed3si9n.expecty.Expecty.expect
44

55
class FmtTests extends munit.FunSuite {
66

7-
val simpleInputsUnformattedContent =
7+
val emptyInputs: TestInputs = TestInputs(Seq(os.rel / ".placeholder" -> ""))
8+
9+
val simpleInputsUnformattedContent: String =
810
"""package foo
911
|
1012
| object Foo extends java.lang.Object {
1113
| def get() = 2
1214
| }
1315
|""".stripMargin
14-
val simpleInputs = TestInputs(
16+
val simpleInputs: TestInputs = TestInputs(
1517
Seq(
1618
os.rel / ".scalafmt.conf" ->
1719
s"""|version = "${Constants.defaultScalafmtVersion}"
@@ -20,7 +22,7 @@ class FmtTests extends munit.FunSuite {
2022
os.rel / "Foo.scala" -> simpleInputsUnformattedContent
2123
)
2224
)
23-
val expectedSimpleInputsFormattedContent = noCrLf {
25+
val expectedSimpleInputsFormattedContent: String = noCrLf {
2426
"""package foo
2527
|
2628
|object Foo extends java.lang.Object {
@@ -29,6 +31,19 @@ class FmtTests extends munit.FunSuite {
2931
|""".stripMargin
3032
}
3133

34+
val simpleInputsWithFilter: TestInputs = TestInputs(
35+
Seq(
36+
os.rel / ".scalafmt.conf" ->
37+
s"""|version = "${Constants.defaultScalafmtVersion}"
38+
|runner.dialect = scala213
39+
|project.excludePaths = [ "glob:**/should/not/format/**.scala" ]
40+
|""".stripMargin,
41+
os.rel / "Foo.scala" -> expectedSimpleInputsFormattedContent,
42+
os.rel / "scripts" / "SomeScript.sc" -> "println()\n",
43+
os.rel / "should" / "not" / "format" / "ShouldNotFormat.scala" -> simpleInputsUnformattedContent
44+
)
45+
)
46+
3247
private def noCrLf(input: String): String =
3348
input.replaceAll("\r\n", "\n")
3449

@@ -57,4 +72,28 @@ class FmtTests extends munit.FunSuite {
5772
}
5873
}
5974

75+
test("filter correctly with --check") {
76+
simpleInputsWithFilter.fromRoot { root =>
77+
val out = os.proc(TestUtil.cli, "fmt", ".", "--check").call(cwd = root).out.text.trim
78+
val outLines = out.linesIterator.toSeq
79+
expect(outLines.length == 2)
80+
expect(outLines.head == "Looking for unformatted files...")
81+
expect(outLines.last == "All files are formatted with scalafmt :)")
82+
}
83+
}
84+
85+
test("--scalafmt-help") {
86+
emptyInputs.fromRoot { root =>
87+
val out1 = os.proc(TestUtil.cli, "fmt", "--scalafmt-help").call(cwd = root).out.text.trim
88+
val out2 = os.proc(TestUtil.cli, "fmt", "-F", "--help").call(cwd = root).out.text.trim
89+
expect(out1.nonEmpty)
90+
expect(out1 == out2)
91+
val outLines = out1.linesIterator.toSeq
92+
val outVersionLine = outLines.head
93+
expect(outVersionLine == s"scalafmt ${Constants.defaultScalafmtVersion}")
94+
val outUsageLine = outLines.drop(1).head
95+
expect(outUsageLine == "Usage: scalafmt [options] [<file>...]")
96+
}
97+
}
98+
6099
}

website/docs/commands/fmt.md

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: Format
33
sidebar_position: 15
44
---
55

6+
import {ChainedSnippets} from "../../src/components/MarkdownComponents.js";
7+
68
Scala CLI supports formatting your code using [Scalafmt](https://scalameta.org/scalafmt/):
79

810
```bash
@@ -23,18 +25,112 @@ scala-cli fmt --check
2325
Scala CLI also supports dialects that are passed to the formatter.
2426
This value is only used if there is no `.scalafmt.conf` file.
2527
However, if it exists, then all configuration should be placed there.
26-
For a list of all possible values, consult the [official Scala Dialects documentation](https://scalameta.org/scalafmt/docs/configuration.html#scala-dialects):
28+
For a list of all possible values, consult
29+
the [official Scala Dialects documentation](https://scalameta.org/scalafmt/docs/configuration.html#scala-dialects):
2730

2831
```bash
2932
scala-cli fmt --dialect scala212
3033
```
3134

3235
### Scalafmt version
3336

34-
At this time, Scala CLI reads a `scalafmt` version from `.scalafmt.conf` files. If the version is missing, Scala CLI throws an error, stating that users should declare an explicit Scalafmt version. Since Scalafmt `3.5.0`, this parameter is mandatory.
37+
At this time, Scala CLI reads a `scalafmt` version from `.scalafmt.conf` files. If the version is missing, Scala CLI
38+
throws an error, stating that users should declare an explicit Scalafmt version. Since Scalafmt `3.5.0`, this parameter
39+
is mandatory.
3540

36-
To configure the Scalafmt version, add the following to `.scalafmt.conf`. For example, to set the version to `3.5.0`, add the following line:
41+
To configure the Scalafmt version, add the following to `.scalafmt.conf`. For example, to set the version to `3.5.0`,
42+
add the following line:
3743

3844
```
3945
version = "3.5.0"
4046
```
47+
48+
### Scalafmt options
49+
50+
It is possible to pass native `scalafmt` options with the `-F` (short for `--scalafmt-arg`), for example:
51+
52+
<ChainedSnippets>
53+
54+
```bash
55+
scala-cli fmt -F --version
56+
```
57+
58+
```text
59+
scalafmt 3.5.2
60+
```
61+
62+
</ChainedSnippets>
63+
64+
For the available options please refer to `scalafmt` help, which can be viewed with the `--scalafmt-help` option (which
65+
is just an alias for `-F --help`):
66+
67+
<ChainedSnippets>
68+
69+
```bash
70+
scala-cli fmt --scalafmt-help
71+
```
72+
73+
```text
74+
scalafmt 3.5.2
75+
Usage: scalafmt [options] [<file>...]
76+
77+
-h, --help prints this usage text
78+
-v, --version print version
79+
(...)
80+
```
81+
82+
</ChainedSnippets>
83+
84+
### Excluding sources
85+
86+
Because of the way Scala CLI invokes `scalafmt` under the hood, sources are always being passed to it explicitly. This
87+
in turn means that regardless of how the sources were passed, `scalafmt` exclusion paths (the `project.excludePaths`)
88+
would be ignored. In order to prevent that from happening, the `--respect-project-filters` option is set to `true` by
89+
default.
90+
91+
```text title=.scalafmt.conf
92+
version = 3.5.2
93+
runner.dialect = scala3
94+
project {
95+
includePaths = [
96+
"glob:**.scala",
97+
"regex:.*\\.sc"
98+
]
99+
excludePaths = [
100+
"glob:**/should/not/format/**.scala"
101+
]
102+
}
103+
```
104+
105+
<ChainedSnippets>
106+
107+
```bash
108+
scala-cli fmt . --check
109+
```
110+
111+
```text
112+
All files are formatted with scalafmt :)
113+
```
114+
115+
</ChainedSnippets>
116+
117+
You can explicitly set it to false if you want to disregard any filters configured in the `project.excludePaths` setting
118+
in your `.scalafmt.conf` for any reason.
119+
120+
<ChainedSnippets>
121+
122+
```bash
123+
scala-cli fmt . --check --respect-project-filters=false
124+
```
125+
126+
```text
127+
--- a/.../should/not/format/ShouldNotFormat.scala
128+
+++ b/.../should/not/format/ShouldNotFormat.scala
129+
@@ -1,3 +1,3 @@
130+
class ShouldNotFormat {
131+
- println()
132+
+ println()
133+
}
134+
```
135+
136+
</ChainedSnippets>

website/docs/reference/cli-options.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,17 @@ Available in commands:
456456

457457
#### `--check`
458458

459-
Check that sources are well formatted
459+
Check if sources are well formatted
460+
461+
#### `--respect-project-filters`
462+
463+
Use project filters defined in the configuration (turned on by default)
464+
465+
#### `--scalafmt-help`
466+
467+
Aliases: `--fmt-help`
468+
469+
Show help for scalafmt. This is an alias for --scalafmt-arg -help
460470

461471
#### `--os-arch-suffix`
462472

0 commit comments

Comments
 (0)