Skip to main content

Format

Scala CLI supports formatting your code using Scalafmt:

scala-cli fmt

Under the hood, Scala CLI downloads and runs Scalafmt on your code.

If you’re setting up a continuous integration (CI) server, Scala CLI also has you covered. You can check formatting correctness using a --check flag:

scala-cli fmt --check

Scalafmt version and dialect

Scala CLI fmt command supports passing the scalafmt version and dialect directly from the command line, using the --scalafmt-dialect and --scalafmt-version options respectively:

scala-cli fmt --scalafmt-dialect scala3 --scalafmt-version 3.5.8

You can skip passing either of those, which will make Scala CLI infer a default value:

  • 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 the Scala version (defined explicitly by -S option, or default version if the option is not 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 found the specified configuration in the .scalafmt.conf file in the workspace.

Example 1

.scalafmt.conf
version = "3.5.8"
runner.dialect = scala212
scala-cli fmt --scalafmt-dialect scala213

For the setup above, fmt will use:

  • version="3.5.8" from the file
  • dialect=scala213, because passed --scalafmt-dialect option overrides dialect found in the file

Example 2

.scalafmt.conf
version = "2.7.5"
scala-cli fmt --scalafmt-version 3.5.8

For the setup above, fmt will use:

  • version="3.5.8", because passed --scalafmt-version option overrides version from the file
  • dialect=scala3, because dialect is neither passed as an option nor is it present in the configuration file, so it is inferred based on the Scala version; the Scala version wasn't explicitly specified in the command either, so it falls back to the default Scala version - the latest one, thus the resulting dialect is scala3.

Scalafmt options

It is possible to pass native scalafmt options with the -F (short for --scalafmt-arg), for example:

scala-cli fmt -F --version
scalafmt 3.5.8

For the available options please refer to scalafmt help, which can be viewed with the --scalafmt-help option (which is just an alias for -F --help):

scala-cli fmt --scalafmt-help
scalafmt 3.5.8
Usage: scalafmt [options] [<file>...]

-h, --help prints this usage text
-v, --version print version
(...)

Excluding sources

Because of the way Scala CLI invokes scalafmt under the hood, sources are always being passed to it explicitly. This in turn means that regardless of how the sources were passed, scalafmt exclusion paths (the project.excludePaths) would be ignored. In order to prevent that from happening, the --respect-project-filters option is set to true by default.

.scalafmt.conf
version = "3.5.8"
runner.dialect = scala3
project {
includePaths = [
"glob:**.scala",
"regex:.*\\.sc"
]
excludePaths = [
"glob:**/should/not/format/**.scala"
]
}
scala-cli fmt . --check
All files are formatted with scalafmt :)

You can explicitly set it to false if you want to disregard any filters configured in the project.excludePaths setting in your .scalafmt.conf for any reason.

scala-cli fmt . --check --respect-project-filters=false
--- a/.../should/not/format/ShouldNotFormat.scala
+++ b/.../should/not/format/ShouldNotFormat.scala
@@ -1,3 +1,3 @@
class ShouldNotFormat {
- println()
+ println()
}

How .scalafmt.conf file is generated

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 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.
  3. Configuration file is not found.
  • In the first case fmt uses the found .scalafmt.conf file to run scalafmt.
  • In the second case fmt creates a .scalafmt.conf file inside the .scala-build directory. Content of the previously found file is copied into the newly created file, missing parameters are inferred and written into the same file. Created file is used to run scalafmt.
  • In the third case fmt creates a .scalafmt.conf file inside the .scala-build directory, writes inferred version and dialect into it and uses it to run scalafmt.

If the --save-scalafmt-conf option is passed, then fmt command behaves as follows:

  • In the first case fmt uses the found .scalafmt.conf file to run scalafmt.
  • In the second case fmt infers 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 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 found the specified configuration in a .scalafmt.conf file in the workspace.