Scalafmt

← 返回目录 > ← 返回总览

相关工具推荐

工具 说明
Scalafix Scala 代码质量和重构检查工具,与 Scalafmt 互补
Spotless 多语言格式化调度插件,可集成 Scalafmt 作为 Scala 格式化引擎
Pre-commit 多语言 Git 钩子管理框架,可通过 local hook 调用 Scalafmt CLI
Prettier 通用代码格式化工具,不支持 Scala,Scalafmt 是 Scala 对应的格式化工具

1. 简介

Scalafmt 是由 Scalameta 团队开发的 Scala 代码格式化工具,是 Scala 生态中的事实标准格式化方案。IntelliJ Scala 插件和 Metals 语言服务器均内置支持,ZIO、Cats、Play Framework 等主流项目均采用。

  • 主要检查语言:Scala(Scala 2.12、2.13、Scala 3)
  • 主要检查能力:Scala 代码自动格式化,支持 Scala 2.12/2.13/Scala 3 多版本 dialect 切换;涵盖代码格式化(自动修复型)
  • 核心检查原理:基于 Scalameta 解析器生成 AST
  • 检查规则/选项:提供数百个配置选项(按 indent、align、newlines、binPack、danglingParentheses、rewrite 等分组组织),全量配置选项
  • GitHub 仓库https://github.com/scalameta/scalafmt(1,488 stars)
  • 开源协议:Apache-2.0
  • 最新稳定版本:3.11.1
  • 运行环境要求:需 JDK 11+;通过 sbt 插件或 CLI 运行
  • 误报率:无

2. 官方文档

资源 链接
官方文档首页 https://scalameta.org/scalafmt/
安装文档 https://scalameta.org/scalafmt/docs/installation.html
配置选项完整文档 https://scalameta.org/scalafmt/docs/configuration.html
GitHub 仓库 https://github.com/scalameta/scalafmt
Releases 页面 https://github.com/scalameta/scalafmt/releases

3. 社区优秀实践

3.1 ZIO

ZIO 是 Scala 函数式编程框架,使用 Scalafmt 统一代码格式,.scalafmt.conf 配置精细。

ZIO 的实践特点:

  • 指定 runner.dialect = scala213 统一 Scala 2.13 语法
  • 配置 align.preset = mostalign.multiline = false 控制对齐策略
  • 启用 rewrite.rules = [RedundantBraces] 在格式化时自动移除冗余大括号
  • 通过 rewriteTokens 将 Unicode 箭头符号( )统一替换为 ASCII 写法

3.2 Cats

Cats 是 Typelevel 生态核心库,使用 Scalafmt 和 Scalafix 管理代码质量。

Cats 的实践特点:

  • 通过 fileOverride 按目录切换 dialect,为 .sbtscala-2.12scala-3 目录设置不同语法版本
  • 启用 rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers] 在格式化时自动重写
  • 配置 runner.dialect = scala213source3 使用 Scala 2.13 + Scala 3 语法兼容模式
  • 通过 runner.dialectOverride.allowSignificantIndentation = false 禁用 Scala 3 缩进语法

3.3 Play Framework

Play Framework 是 Scala/Web 领域主流 Web 框架,使用 Scalafmt 统一代码格式。

Play Framework 的实践特点:

  • 使用 preset = default 作为基础预设,减少自定义配置量
  • 配置 rewrite.rules 包含 AvoidInfixRedundantParensSortModifiersPreferCurlyForsImports
  • 通过 rewrite.sortModifiers.order 自定义修饰符排序顺序
  • 通过 rewrite.imports 配置 import 展开和分组策略
  • 使用 fileOverride 为 Scala 3 目录切换 dialect

4. 工具配置说明

4.1 配置文件说明

Scalafmt 涉及以下配置文件:

配置文件 用途 使用场景
.scalafmt.conf Scalafmt 主配置文件(HOCON 格式),控制格式化版本、方言、列宽、对齐等行为 所有项目,控制代码格式化行为

配置文件默认位置:仓库根目录 .scalafmt.conf(HOCON 格式)。

4.2 .scalafmt.conf 配置详解

.scalafmt.conf 是 Scalafmt 的主配置文件,用于控制代码格式化行为。

配置项说明

配置项 类型 说明
version string Scalafmt 版本号,建议锁定以避免跨环境格式化差异
runner.dialect string Scala 方言(如 scala213scala3scala213source3sbt
runner.dialectOverride.allowSignificantIndentation bool 是否允许 Scala 3 显著缩进语法(默认 true)
maxColumn int 最大列宽(默认 80)
preset string 预设配置(如 defaultdefaultWithAlign),减少自定义配置量
align.preset string 对齐策略(如 nonesomemostmore
align.multiline bool 多行结构是否对齐
fileOverride map 按目录覆盖配置,键为 glob 模式,值为该目录专属配置块
project.excludeFilters list[string] 排除格式化的文件或目录 glob 模式

推荐配置示例

# .scalafmt.conf
version = "3.11.1"
runner.dialect = scala213
maxColumn = 120
preset = default

完整配置选项列表请参阅官方文档:Configuration


5. 主流集成方式

5.1 sbt 集成(生态主流方式)

sbt 是 Scala 生态事实标准构建工具,Scalafmt 通过官方 sbt-scalafmt 插件集成。

project/plugins.sbt 中添加插件:

addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.1")

常用 sbt 任务:

任务 作用
sbt scalafmt 格式化所有 Scala 源文件(自动修复)
sbt scalafmtCheck 检查格式是否合规(不修改,CI 用)
sbt scalafmtSbt 格式化 .sbtproject/*.scala 文件
sbt scalafmtSbtCheck 检查 sbt 文件格式(不修改)
sbt scalafmtAll 格式化所有配置(含 Test)的源文件
sbt scalafmtCheckAll 检查所有配置的格式
sbt scalafmtOnly <file> 格式化指定文件

增量检查:sbt-scalafmt 插件的 scalafmtOnly 任务支持指定文件,实现文件级增量格式化:

sbt "scalafmtOnly src/main/scala/com/example/Foo.scala"

通过 pre-commit local hook 集成 Scalafmt 时(hook 通过 sbt scalafmt 调用 sbt 任务),pre-commit 框架默认只对暂存区中变更的 .scala 文件触发 hook。由于 Scalafmt 的 local hook 使用 pass_filenames: false(让 sbt 自行管理检查范围),实际增量由 sbt 的构建范围决定。

# .pre-commit-config.yaml
- repo: local
  hooks:
    - id: scalafmt
      name: Scalafmt
      entry: sbt scalafmt
      language: system
      files: \.scala$
      pass_filenames: false # 由 sbt 控制检查范围

pre-commit 负责"何时触发"(只在有 .scala 文件变更时),sbt 负责"检查什么"。如需更精确的文件级增量,可改用 Scalafmt CLI 的 --mode changed 参数:entry: scalafmt --mode changed,此时设置 pass_filenames: false 让 Scalafmt 自行通过 Git diff 确定变更文件。CI 中可通过 pre-commit run --all-files 做全量检查,或使用 --from-ref/--to-ref 做 PR 级增量。

5.2 IDE 集成

IDE 集成方式
IntelliJ IDEA 内置 Scala 插件自带 Scalafmt 支持,在 Settings → Editor → Code Style → Scala 中启用 "Scalafmt"
VS Code 安装 Metals 扩展,Metals 内置 Scalafmt 支持,保存时自动格式化
Neovim 通过 nvim-metals LSP 集成 Scalafmt

5.3 CLI 集成

通过 Coursier 安装 CLI:

cs install scalafmt

常用 CLI 命令:

# 格式化当前目录下所有 .scala 文件(读取 .scalafmt.conf)
scalafmt

# 仅检查不修改(CI 用,失败返回非 0)
scalafmt --test

# 只格式化 git diff 中改动的文件
scalafmt --mode changed

# 格式化指定文件
scalafmt path/to/File.scala

增量检查:Scalafmt CLI 原生支持文件级增量参数,无需额外脚本即可只处理变更文件:

参数/模式 作用 示例
--mode changed 只格式化 git diff/status 中改动的文件 scalafmt --mode changed
--mode diff 只格式化与 git HEAD 有差异的文件 scalafmt --mode diff
--test 仅检查不修改,失败返回非 0 scalafmt --test
直接传文件路径 只格式化指定文件 scalafmt File1.scala File2.scala

也可结合 git diff 筛选变更文件后传递给 scalafmt:

# 只检查 git 暂存区中变更的 .scala 文件
git diff --name-only --diff-filter=ACMR --cached -- '*.scala' | xargs scalafmt --test

CI 脚本调用:在 CI 环境中通过脚本调用 Scalafmt(或 sbt 任务)进行格式化检查。

GitHub Actions 示例:

name: Scalafmt Check
on: [pull_request]
jobs:
  format:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: "21"
      - uses: sbt/setup-sbt@v1
      - run: sbt scalafmtCheckAll

增量检查:GitHub Actions 中按 PR 变更文件触发增量检查:

- name: Check changed files only
  run: |
    CHANGED=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }} -- '*.scala' | tr '\n' ' ')
    if [ -n "$CHANGED" ]; then
      scalafmt --test $CHANGED
    fi

5.4 Scala CLI 集成

Scala CLI 内置格式化命令:

scala-cli format .
scala-cli format --check .

5.5 Gradle/Maven 集成(通过 Spotless)

Gradle 和 Maven 项目通过 Spotless 集成 Scalafmt:

Gradle build.gradle.kts

plugins {
    id("com.diffplug.spotless") version "7.0.2"
    scala
}

spotless {
    scala {
        scalafmt()
    }
}
./gradlew spotlessApply    # 格式化
./gradlew spotlessCheck    # 检查

6. 告警抑制(屏蔽)方法

6.1 通过工具配置文件屏蔽

.scalafmt.conf 中通过 project.excludeFilters 排除特定文件或目录:

# .scalafmt.conf
project.excludeFilters = [
  "target/",
  "generated-sources/",
  ".metals/"
]

官方文档:project.excludeFilters

6.2 通过 fileOverride 按目录切换规则

通过 fileOverride 为不同目录指定不同配置,避免全局调整:

fileOverride {
  "glob:**/src/main/scala-3/**" {
    runner.dialect = scala3
  }
}

官方文档:fileOverride

6.3 通过 CLI 参数屏蔽

# 排除指定文件
scalafmt --exclude "target/**/*.scala" --exclude "generated/**"

# 只处理匹配的文件
scalafmt --include "src/main/**/*.scala"

官方文档:CLI 选项

6.4 通过构建配置屏蔽

sbt 项目中可通过 scalafmtConfigscalafmtFilter 调整作用范围:

// build.sbt:排除特定目录
ThisBuild / scalafmtFilter = "diff:jvm"