Skip to content

Commit e00f262

Browse files
Add fmt command
1 parent 8e0b4c6 commit e00f262

File tree

8 files changed

+206
-3
lines changed

8 files changed

+206
-3
lines changed

build.sc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,23 @@ object `generate-reference-doc` extends SbtModule {
100100
}
101101

102102
object dummy extends Module {
103-
// dummy project to get scala steward updates for Ammonite, whose
104-
// version is used in the repl command, and ensure Ammonite is available
105-
// for all Scala versions we support
103+
// dummy projects to get scala steward updates for Ammonite and scalafmt, whose
104+
// versions are used in the fmt and repl commands, and ensure Ammonite is available
105+
// for all Scala versions we support.
106106
object amm extends Cross[Amm](Scala.listAll: _*)
107107
class Amm(val crossScalaVersion: String) extends CrossScalaModule with Bloop.Module {
108108
def skipBloop = true
109109
def ivyDeps = Agg(
110110
Deps.ammonite
111111
)
112112
}
113+
object scalafmt extends ScalaModule with Bloop.Module {
114+
def skipBloop = true
115+
def scalaVersion = Scala.defaultInternal
116+
def ivyDeps = Agg(
117+
Deps.scalafmtCli
118+
)
119+
}
113120
}
114121

115122
class BuildMacros(val crossScalaVersion: String) extends CrossSbtModule with ScalaCliPublishModule {
@@ -221,6 +228,8 @@ class Build(val crossScalaVersion: String)
221228
|
222229
| def ammoniteVersion = "${Deps.ammonite.dep.version}"
223230
|
231+
| def defaultScalafmtVersion = "${Deps.scalafmtCli.dep.version}"
232+
|
224233
| def defaultScalaVersion = "${Scala.defaultUser}"
225234
|}
226235
|""".stripMargin

modules/cli/src/main/scala/scala/cli/ScalaCli.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ object ScalaCli extends CommandsEntryPoint {
2121
Compile,
2222
Directories,
2323
Export,
24+
Fmt,
2425
InstallCompletions,
2526
Metabrowse,
2627
Repl,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
import scala.build.Inputs
6+
import scala.build.internal.Runner
7+
import scala.cli.internal.FetchExternalBinary
8+
9+
object Fmt extends ScalaCommand[FmtOptions] {
10+
override def group = "Miscellaneous"
11+
override def sharedOptions(options: FmtOptions) = Some(options.shared)
12+
def run(options: FmtOptions, args: RemainingArgs): Unit = {
13+
14+
// TODO If no input is given, just pass '.' to scalafmt?
15+
val (sourceFiles, workspace) =
16+
if (args.remaining.isEmpty)
17+
(Seq(os.pwd), os.pwd)
18+
else {
19+
val i = options.shared.inputsOrExit(args)
20+
val s = i.sourceFiles().collect {
21+
case sc: Inputs.Script => sc.path
22+
case sc: Inputs.ScalaFile => sc.path
23+
}
24+
(s, i.workspace)
25+
}
26+
27+
val logger = options.shared.logger
28+
val cache = options.shared.coursierCache
29+
30+
if (sourceFiles.isEmpty)
31+
logger.debug("No source files, not formatting anything")
32+
else {
33+
34+
val fmtLauncher = options.scalafmtLauncher.filter(_.nonEmpty) match {
35+
case Some(launcher) =>
36+
os.Path(launcher, os.pwd)
37+
case None =>
38+
val (url, changing) = options.binaryUrl
39+
FetchExternalBinary.fetch(url, changing, cache, logger, "scalafmt")
40+
}
41+
42+
logger.debug(s"Using scalafmt launcher $fmtLauncher")
43+
44+
val command = Seq(fmtLauncher.toString) ++ sourceFiles.map(_.toString)
45+
Runner.run(
46+
"scalafmt",
47+
command,
48+
logger,
49+
allowExecve = true,
50+
cwd = Some(workspace)
51+
)
52+
}
53+
}
54+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
import scala.build.internal.Constants
6+
import scala.cli.internal.FetchExternalBinary
7+
import scala.util.Properties
8+
9+
// format: off
10+
@HelpMessage("Format Scala code")
11+
final case class FmtOptions(
12+
@Recurse
13+
shared: SharedOptions = SharedOptions(),
14+
@HelpMessage("Check that sources are well formatted")
15+
check: Boolean = false,
16+
17+
@Hidden
18+
osArchSuffix: Option[String] = None,
19+
@Hidden
20+
scalafmtTag: Option[String] = None,
21+
@Hidden
22+
scalafmtGitHubOrgName: Option[String] = None,
23+
@Hidden
24+
scalafmtExtension: Option[String] = None,
25+
@Hidden
26+
scalafmtLauncher: Option[String] = None
27+
) {
28+
// format: on
29+
30+
def binaryUrl: (String, Boolean) = {
31+
val osArchSuffix0 = osArchSuffix.map(_.trim).filter(_.nonEmpty)
32+
.getOrElse(FetchExternalBinary.platformSuffix())
33+
val tag0 = scalafmtTag.getOrElse("v" + Constants.defaultScalafmtVersion)
34+
val gitHubOrgName0 = scalafmtGitHubOrgName.getOrElse("alexarchambault/scalafmt-native-image")
35+
val extension0 = if (Properties.isWin) ".zip" else ".gz"
36+
val url =
37+
s"https://github.com/$gitHubOrgName0/releases/download/$tag0/scalafmt-$osArchSuffix0$extension0"
38+
(url, !tag0.startsWith("v"))
39+
}
40+
41+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package scala.cli.integration
2+
3+
import com.eed3si9n.expecty.Expecty.expect
4+
5+
class FmtTests extends munit.FunSuite {
6+
7+
val simpleInputs = TestInputs(
8+
Seq(
9+
os.rel / ".scalafmt.conf" ->
10+
"""runner.dialect = scala213
11+
|""".stripMargin,
12+
os.rel / "Foo.scala" ->
13+
"""package foo
14+
|
15+
| object Foo extends java.lang.Object {
16+
| def get() = 2
17+
| }
18+
|""".stripMargin
19+
)
20+
)
21+
val expectedSimpleInputsFormattedContent = noCrLf {
22+
"""package foo
23+
|
24+
|object Foo extends java.lang.Object {
25+
| def get() = 2
26+
|}
27+
|""".stripMargin
28+
}
29+
30+
private def noCrLf(input: String): String =
31+
input.replaceAll("\r\n", "\n")
32+
33+
test("simple") {
34+
simpleInputs.fromRoot { root =>
35+
os.proc(TestUtil.cli, "fmt", ".").call(cwd = root)
36+
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
37+
expect(updatedContent == expectedSimpleInputsFormattedContent)
38+
}
39+
}
40+
41+
test("no inputs") {
42+
simpleInputs.fromRoot { root =>
43+
os.proc(TestUtil.cli, "fmt").call(cwd = root)
44+
val updatedContent = noCrLf(os.read(root / "Foo.scala"))
45+
expect(updatedContent == expectedSimpleInputsFormattedContent)
46+
}
47+
}
48+
49+
}

project/deps.sc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ object Deps {
5252
def scala3Compiler(sv: String) = ivy"org.scala-lang::scala3-compiler:$sv"
5353
def scalaAsync = ivy"org.scala-lang.modules::scala-async:0.10.0"
5454
def scalac(sv: String) = ivy"org.scala-lang:scala-compiler:$sv"
55+
def scalafmtCli = ivy"org.scalameta::scalafmt-cli:3.0.3"
5556
def scalaJsEnvNodeJs = ivy"org.scala-js::scalajs-env-nodejs:1.1.1"
5657
def scalaJsLinker = ivy"org.scala-js::scalajs-linker:${Versions.scalaJs}"
5758
def scalaJsLinkerInterface = ivy"org.scala-js::scalajs-linker-interface:${Versions.scalaJs}"

website/docs/reference/cli-options.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Available in commands:
5353
- [`bsp`](./commands#bsp)
5454
- [`compile`](./commands#compile)
5555
- [`export`](./commands#export)
56+
- [`fmt`](./commands#fmt)
5657
- [`browse` / `metabrowse`](./commands#browse)
5758
- [`package`](./commands#package)
5859
- [`console` / `repl`](./commands#console)
@@ -126,6 +127,7 @@ Available in commands:
126127
- [`bsp`](./commands#bsp)
127128
- [`compile`](./commands#compile)
128129
- [`export`](./commands#export)
130+
- [`fmt`](./commands#fmt)
129131
- [`browse` / `metabrowse`](./commands#browse)
130132
- [`package`](./commands#package)
131133
- [`console` / `repl`](./commands#console)
@@ -156,6 +158,7 @@ Available in commands:
156158
- [`bsp`](./commands#bsp)
157159
- [`compile`](./commands#compile)
158160
- [`export`](./commands#export)
161+
- [`fmt`](./commands#fmt)
159162
- [`browse` / `metabrowse`](./commands#browse)
160163
- [`package`](./commands#package)
161164
- [`console` / `repl`](./commands#console)
@@ -192,6 +195,28 @@ Available in commands:
192195

193196
Aliases: `-o`
194197

198+
## Fmt options
199+
200+
Available in commands:
201+
- [`fmt`](./commands#fmt)
202+
203+
204+
<!-- Automatically generated, DO NOT EDIT MANUALLY -->
205+
206+
#### `--check`
207+
208+
Check that sources are well formatted
209+
210+
#### `--os-arch-suffix`
211+
212+
#### `--scalafmt-tag`
213+
214+
#### `--scalafmt-git-hub-org-name`
215+
216+
#### `--scalafmt-extension`
217+
218+
#### `--scalafmt-launcher`
219+
195220
## Help options
196221

197222
Available in commands:
@@ -204,6 +229,7 @@ Available in commands:
204229
- [`compile`](./commands#compile)
205230
- [`directories`](./commands#directories)
206231
- [`export`](./commands#export)
232+
- [`fmt`](./commands#fmt)
207233
- [`install completions`](./commands#install-completions)
208234
- [`browse` / `metabrowse`](./commands#browse)
209235
- [`package`](./commands#package)
@@ -280,6 +306,7 @@ Available in commands:
280306
- [`bsp`](./commands#bsp)
281307
- [`compile`](./commands#compile)
282308
- [`export`](./commands#export)
309+
- [`fmt`](./commands#fmt)
283310
- [`browse` / `metabrowse`](./commands#browse)
284311
- [`package`](./commands#package)
285312
- [`console` / `repl`](./commands#console)
@@ -321,6 +348,7 @@ Available in commands:
321348
- [`clean`](./commands#clean)
322349
- [`compile`](./commands#compile)
323350
- [`export`](./commands#export)
351+
- [`fmt`](./commands#fmt)
324352
- [`install completions`](./commands#install-completions)
325353
- [`browse` / `metabrowse`](./commands#browse)
326354
- [`package`](./commands#package)
@@ -588,6 +616,7 @@ Available in commands:
588616
- [`bsp`](./commands#bsp)
589617
- [`compile`](./commands#compile)
590618
- [`export`](./commands#export)
619+
- [`fmt`](./commands#fmt)
591620
- [`browse` / `metabrowse`](./commands#browse)
592621
- [`package`](./commands#package)
593622
- [`console` / `repl`](./commands#console)
@@ -620,6 +649,7 @@ Available in commands:
620649
- [`bsp`](./commands#bsp)
621650
- [`compile`](./commands#compile)
622651
- [`export`](./commands#export)
652+
- [`fmt`](./commands#fmt)
623653
- [`browse` / `metabrowse`](./commands#browse)
624654
- [`package`](./commands#package)
625655
- [`console` / `repl`](./commands#console)
@@ -658,6 +688,7 @@ Available in commands:
658688
- [`bsp`](./commands#bsp)
659689
- [`compile`](./commands#compile)
660690
- [`export`](./commands#export)
691+
- [`fmt`](./commands#fmt)
661692
- [`browse` / `metabrowse`](./commands#browse)
662693
- [`package`](./commands#package)
663694
- [`console` / `repl`](./commands#console)
@@ -698,6 +729,7 @@ Available in commands:
698729
- [`bsp`](./commands#bsp)
699730
- [`compile`](./commands#compile)
700731
- [`export`](./commands#export)
732+
- [`fmt`](./commands#fmt)
701733
- [`browse` / `metabrowse`](./commands#browse)
702734
- [`package`](./commands#package)
703735
- [`console` / `repl`](./commands#console)

website/docs/reference/commands.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ Accepts options:
6262
- [scalac](./cli-options.md#scalac-options)
6363
- [shared](./cli-options.md#shared-options)
6464

65+
## `fmt`
66+
67+
Format Scala code
68+
69+
Accepts options:
70+
- [compilation server](./cli-options.md#compilation-server-options)
71+
- [coursier](./cli-options.md#coursier-options)
72+
- [dependency](./cli-options.md#dependency-options)
73+
- [fmt](./cli-options.md#fmt-options)
74+
- [jvm](./cli-options.md#jvm-options)
75+
- [logging](./cli-options.md#logging-options)
76+
- [Scala.JS](./cli-options.md#scalajs-options)
77+
- [Scala Native](./cli-options.md#scala-native-options)
78+
- [scalac](./cli-options.md#scalac-options)
79+
- [shared](./cli-options.md#shared-options)
80+
6581
## `install completions`
6682

6783
Accepts options:

0 commit comments

Comments
 (0)