agustafson/prince-of-space

GitHub: agustafson/prince-of-space

一款受 Prettier 启发的 Java 代码格式化工具,以最少配置和合理默认值终结团队代码风格争论。

Stars: 0 | Forks: 0

# 太空王子 Prince of Space [![Maven Central](https://img.shields.io/maven-central/v/io.github.agustafson.princeofspace/prince-of-space-core.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.agustafson.princeofspace/prince-of-space-core) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/340c7ce57d155612.svg)](https://github.com/agustafson/prince-of-space/actions/workflows/ci.yml) [![许可证](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) 一个简单且可配置的 Java 代码格式化工具,能够生成排列整齐的代码。([`io.github.agustafson.princeofspace`](https://central.sonatype.com/namespace/io.github.agustafson.princeofspace))。 ## 为什么需要另一个格式化工具? Java 几乎是主流语言中唯一没有公认默认格式化工具的语言。JavaScript 有 [Prettier](https://prettier.io/)。Kotlin 有 [ktlint](https://pinterest.github.io/ktlint/)。Go 在标准工具链中附带了 `gofmt`。Java 却只有……无休止的争论。 现有的选项并不能很好地填补这一空白。这里有一篇很好的总结:https://jqno.nl/post/2024/08/24/why-are-there-no-decent-code-formatters-for-java/。Google-java-format 完全不可配置,而且它对双缩进的偏好使得生成的代码难以阅读。Eclipse 和 IntelliJ 内置的格式化工具提供了数百个调整旋钮,这听起来很灵活,但实际上意味着每个团队都会以不同的方式配置它们,而“格式化工具”反而成为了风格争论的另一个源头,而不是平息争论的终点。 Prince of Space 汲取了 Prettier 和 ktlint 的理念:强大、易读的默认设置,仅提供足够的配置来解决团队真正存在分歧的少数问题。以合理的默认值提供最少的选项。 ## 功能 - **7 个配置选项**,涵盖缩进、行长、换行和尾随逗号 - **单阈值行长** — 一个 `lineLength` 目标使得换行更具可预测性,并缩小了配置范围 - **幂等(不动点输出)** — 格式化一次后,再次运行格式化工具不会做任何进一步修改(`format(format(x)) == format(x)`)。默认引擎在内部**收敛**(在预算范围内重复遍历直到稳定),因此通常一次调用即可满足要求;参见 [`docs/canonical-formatting-rules.md`](docs/canonical-formatting-rules.md) 中的规则 1。 - **支持 Java 8 到 25+** — 可以解析任何 Java 语言级别;运行环境需 JDK 17+ - **多种集成方式** — 库 API、CLI、Spotless 插件、IntelliJ 插件、VS Code 扩展 ## 快速开始 ### 库 (Gradle) ``` dependencies { implementation("io.github.agustafson.princeofspace:prince-of-space-core:2.1.2") } ``` ``` import io.princeofspace.Formatter; import io.princeofspace.model.FormatterConfig; Formatter formatter = new Formatter(FormatterConfig.defaults()); String formatted = formatter.format(sourceCode); ``` ### 库 (Maven) ``` io.github.agustafson.princeofspace prince-of-space-core 2.1.2 ``` ### CLI ``` ./gradlew :cli:shadowJar java -jar modules/cli/build/libs/prince-of-space-cli-*.jar --help ``` 常用标志: | 标志 | 描述 | |------|-------------| | `--check` | 如果任何文件会被更改,则以退出码 1 退出(不写入) | | `--stdin` | 从标准输入读取,写入标准输出 | | `--java-version N` | Java 语言级别 (8, 11, 17, 21, 25 等) | | `-r` | 递归进入目录 | | `-v` | 在标准错误输出显示详细进度 | ### Spotless ``` import io.princeofspace.model.FormatterConfig import io.princeofspace.spotless.PrinceOfSpaceStep spotless { java { target("src/**/*.java") addStep(PrinceOfSpaceStep.create(FormatterConfig.defaults())) } } ``` 将 Spotless 模块放在你的构建导入 `PrinceOfSpaceStep` 的 classpath 上 — 例如 `buildSrc` / `implementation`,或根据你的 Gradle 布局使用 `buildscript { dependencies { classpath(...) } }`。使用 `io.github.agustafson.princeofspace:prince-of-space-spotless:2.1.2`(固定为 Maven Central 上的版本)。Maven:将相同的坐标添加为 `spotless-maven-plugin` 的依赖,然后在插件配置中使用 `PrinceOfSpaceStep.create(...)`。 ### IntelliJ 插件 **Settings > Tools > Prince of Space** — 配置全部 7 个选项,选择固定的 Java 级别或从模块继承,并可选择在保存时格式化。通过 **Code > Reformat with Prince of Space...** 进行格式化 ``` ./gradlew :intellij-plugin:runIde # develop ./gradlew :intellij-plugin:buildPlugin # package ``` ### VS Code 扩展 `modules/vscode-extension/` 目录包含一个注册了 Java 格式化提供程序的 TypeScript 扩展。它委托给 CLI shadow JAR,从工作区解析 `modules/cli/build/libs/prince-of-space-cli-*.jar`,除非设置了 `princeOfSpace.cliJar`。 ## 配置 | 选项 | 默认值 | 描述 | |--------|---------|-------------------------------------------------------| | `wrapStyle` | `balanced` | `wide`、`balanced` 或 `narrow` 换行 | | `indentStyle` | `spaces` | `spaces` 或 `tabs` | | `indentSize` | `4` | 每缩进级别的单位(空格或制表符) | | `lineLength` | `120` | 目标行宽 — 换行在此触发 | | `closingParenOnNewLine` | `true` | 参数换行时将 `)` 放在单独的一行 | | `trailingCommas` | `false` | 多行 enums/arrays 中的尾随逗号 | | `javaLanguageLevel` | `17` | 解析器接受的 Java 语法级别 | 此表中的数字和枚举默认值与源代码中的公共 `FormatterConfig.DEFAULT_*` 常量相匹配(单一事实来源)。 延续缩进始终为 `2 * indentSize`(默认为 8 个空格),用于带分隔符的列表延续(参数、实参、二元表达式、三元表达式等)。这遵循了 Oracle/IntelliJ 的惯例,并确保参数在视觉上始终与方法体有所区分。编程访问:`FormatterConfig#continuationIndentSize()`。换行的链式方法调用则使用单个 `indentSize` 步进 — 参见 [`docs/formatting-rules.md`](docs/formatting-rules.md) 中的“方法链”和 TDR-015。 ### 换行风格 `wrapStyle` 控制触发换行后元素在各行中的分布方式。这是影响最大的选项 — 同样的代码在不同风格下看起来会大相径庭。 **`balanced`**(默认) — 要么全有要么全无:要么所有内容都放在同一行,要么每个元素独占一行。这就是 [Prettier 的方法](https://prettier.io/docs/option-philosophy):它避免了某些参数在同一行而其他参数在下一行的混乱中间状态。 ``` // fits on one line — left alone doSomething(name, age, active); // does not fit — every element gets its own line doSomething( name, age, active ); ``` **`wide`** — 尽量将内容保留在同一行;仅对为保持在行长限制内所必需的部分进行换行。 ``` doSomething(name, age, active, extraParam); ``` **`narrow`** — 如果需要任何换行,则立即将每个元素放在单独的一行。 ``` doSomething( name, age, active ); ``` `javaLanguageLevel`(默认值:`17`)控制解析器接受哪种 Java 语法。在 API 中通过 `FormatterConfig.builder().javaLanguageLevel(JavaLanguageLevel.of(21))` 设置,或在 CLI 中通过 `--java-version 21` 设置。 ## 示例 `examples/` 目录是评估各项选项如何影响实际输出的最佳方式: - **`examples/inputs/java{8,17,21,25}/FormatterShowcase.java`** — 一个涵盖 46+ 种场景的单一未格式化源文件:构造函数、方法链、lambdas、二元运算符、泛型、switch 表达式、records、sealed 类型、文本块等。 - **`examples/outputs/java{8,17,21,25}/`** — 每个 Java 级别有 6 个格式化版本(共 24 个),分别对应 `wrapStyle` 和 `closingParenOnNewLine` 的每种组合。 - **`examples/external/outputs/`** — 通过 [Spotless](https://github.com/diffplug/spotless) 使用其他流行的 Java 格式化工具(Google Java Format AOSP、Eclipse JDT、Palantir Java Format AOSP、带有 prettier-plugin-java 的 Prettier)处理的相同展示文件,每个格式化工具每个 Java 级别一个文件 — 在比较格式化工具时非常有用。 在浏览器中打开 **[`examples/compare.html`](examples/compare.html)**(或此仓库的 GitHub Pages 副本)以查看交互式的并排对比:选择一个 Java 级别,然后在左侧和右侧各选任意两个输出 — Prince of Space 配置 **或** 上述基于 Spotless 的替代方案之一 — 以查看它们在相同输入上的差异。要获取侧重于 Prince of Space 选项的带解说演练,请参阅 **[docs/output-showcase.md](docs/output-showcase.md)**。 ## 性能 (Spring Framework) 在此语料库上,**strict**(不动点)模式的 Prince of Space 比 Eclipse JDT 慢,并且比 Google Java Format 和 Palantir 稍慢;**fast** 单次处理模式的速度则与这些 JVM 格式化工具大致相当。默认引擎优先保证幂等性(规则 1)而不是原始吞吐量。在实践中,Spotless 通常只格式化更改过的文件,这使实际执行时间保持在较短水平。 _从 [`docs/perf-results/spring-framework.md`](docs/perf-results/spring-framework.md) 自动生成 — 请勿在标记之间编辑;运行 `./gradlew refreshSpringBenchmarkReadme` 或在 CI 中执行。_ [Spring Framework](https://github.com/spring-projects/spring-framework) 语料库上的吞吐量(**39ff8e4**,**9204** 个文件,与外部评估工具使用相同的路径过滤器)。**平均毫秒/文件** 是该次运行的总 JVM 实际时间除以文件数量。每个 JVM 格式化工具在一个进程中运行并进行预热;JVM:**21.0.10+7-LTS** Prince of Space 使用 `FormatterConfig.defaults()`(行长 **120**,换行 **BALANCED**,Java 语言级别 **17**)。Google Java Format 和 Palantir Java Format 使用 **AOSP** 风格,以与 `examples/external/outputs/` 的 Spotless 对比保持一致。Eclipse JDT 使用 Spotless `eclipse()` 默认设置(`examples/external/outputs/eclipse/`)。 ## 结果 | 格式化工具 | 平均毫秒/文件 | 失败数 | |-----------|-----------------|----------| | Prince of Space (strict, 不动点) | 6.24 | 0 | | Prince of Space (fast, 单次处理) | 2.51 | 0 | | Google Java Format (AOSP) | 2.69 | 0 | | Palantir Java Format (AOSP) | 2.73 | 0 | | Eclipse JDT (Spotless default) | 2.21 | 0 | 完整细节和重新生成方法:运行 `./gradlew :formatter-benchmark:run`,并使用 `PRINCE_BENCH_ROOT` 指向一个代码库检出副本。_此运行中跳过了 Prettier 吞吐量测试 (`PRINCE_BENCH_SKIP_PRETTIER=true`)。_ Eclipse JDT 使用了与 [`examples/external/outputs/eclipse/`](examples/external/outputs/eclipse/) 相同的 Spotless `eclipse()` 栈(首次运行使用 Equo P2 + Maven Central)。 _报告日期:**2026-05-03**。_ ## API 公共 API 由四种类型组成: | 类型 | 描述 | |------|-------------| | `Formatter` | 入口点 — `format(String)` 在失败时抛出异常,`formatResult(String)` 返回一个密封结果 | | `FormatterConfig` | 不可变记录,包含所有 7 个选项 + 语言级别的构建器 | | `FormatResult` | 密封接口:`Success` 或 `Failure`(`ParseFailure`、`EmptyCompilationUnit`) | | `FormatterException` | 在解析或管道失败时由 `Formatter.format()` 抛出 | 支持值类型:`IndentStyle`、`WrapStyle`、`JavaLanguageLevel`。 ### strict 默认模式与 fast 单次处理模式(`Formatter` 重载) `new Formatter(FormatterConfig)` 会运行引擎,直到输出**收敛**(在 `prince.maxConvergencePasses` 范围内的不动点),这是使**规则 1** 幂等性在日常使用中得以成立的保证。 带有 `fastSinglePass=true` 的 `new Formatter(FormatterConfig, boolean fastSinglePass)` 执行**一次**解析→打印过程 — 适用于与其他单次调用的 JVM 格式化工具进行吞吐量基准测试(`:formatter-benchmark`)。它**不是**七个格式化选项之外的第二个公共配置旋钮:在 `FormatterConfig`、Spotless 或 IDE 插件中暴露“fast”模式会诱使集成工具为了速度而输出非不动点的结果。建议将快速模式保留用于实验和愿意接受这种权衡的高级调用者(参见 [`docs/technical-decision-register.md`](docs/technical-decision-register.md) 中的 TDR-026)。 ### 非抛出异常型 API ``` FormatResult result = formatter.formatResult(sourceCode); if (result instanceof FormatResult.Success success) { System.out.println(success.formattedSource()); } else if (result instanceof FormatResult.ParseFailure failure) { System.err.println(failure.message()); } ``` ## 构件 (Maven Central) Group ID:**`io.agustafson.princeofspace`**。发布的版本显示在 [Maven Central](https://central.sonatype.com/namespace/io.github.agustafson.princeofspace) 上。快速开始的坐标从 `gradle.properties` 中的 **`readmeMavenCoordinatesVersion`** 同步(最后一次 Central 发布 — 更改后需运行 `./gradlew syncReadmeVersions`)。开发构建使用同一文件中的 `version=`(通常是 `*-SNAPSHOT`),并且不会出现在 README 代码片段中。 | 构件 | 坐标 | 何时使用 | |----------|------------|-------------| | `prince-of-space-core` | `io.github.agustafson.princeofspace:prince-of-space-core:2.1.2` | 默认 — 体积小;JavaParser + SLF4J 作为常规传递依赖 | | `prince-of-space-bundled` | `io.github.agustafson.princeofspace:prince-of-space-bundled:2.1.2` | 单个胖 JAR,依赖项已重定位 — 无 classpath 冲突 | | `prince-of-space-spotless` | `io.github.agustafson.princeofspace:prince-of-space-spotless:2.1.2` | Spotless `FormatterStep` (`PrinceOfSpaceStep`) | | CLI (shadow JAR) | 从仓库构建或附加至 [GitHub Releases](https://github.com/agustafson/prince-of-space/releases) | 命令行格式化;并不总是发布到 Central | ## 非目标 - 组织 Java 导入(委托给 Spotless) - 第一方的 Maven/Gradle 插件(由 Spotless 提供) - 类型解析(格式化不需要) ## 从源码构建 需要 JDK 21+。通过 `--release 17` 将发布的字节码目标定为 Java 17。 ``` ./gradlew build # full build: compile, test, Spotless, Checkstyle, SpotBugs ./gradlew :core:test # fast feedback loop for core changes ``` 有关提交约定和 PR 要求,请参阅 [docs/contributing.md](docs/contributing.md)。 ## 文档 完整索引请参阅 **[docs/index.md](docs/index.md)**。主要文档: | 文档 | 内容 | |----------|----------| | [docs/architecture.md](docs/architecture.md) | 包布局、编码约定、模块结构 | | [docs/contributing.md](docs/contributing.md) | 提交约定、构建要求、PR 检查 | | [CHANGELOG.md](CHANGELOG.md) | 发布历史 | | [SECURITY.md](SECURITY.md) | 漏洞报告 | | [docs/formatting-rules.md](docs/formatting-rules.md) | 所有格式化规则和配置选项 | | [docs/evaluation.md](docs/evaluation.md) | 真实世界评估工具及最新结果 (Guava + Spring) | | [docs/benchmarks.md](docs/benchmarks.md) | 吞吐量工具 (`:formatter-benchmark`) 和冒烟测试时间 | | [docs/perf-results/spring-framework.md](docs/perf-results/spring-framework.md) | 最新 Spring Framework 语料库与其他格式化工具的对比 | | [docs/technical-decision-register.md](docs/technical-decision-register.md) | 架构决策日志 | ## 许可证 [Apache License 2.0](LICENSE)
标签:IDE插件, Java代码格式化工具, JS文件枚举, Prettier替代, 代码排版, 代码格式化, 代码美化, 代码规范, 代码风格配置, 后台面板检测, 域名枚举, 威胁情报, 开发者工具, 开源, 开源框架, 持续集成, 格式化器, 错误基检测, 静态代码分析