lint 格式化器
@ttsc/lint 自带一个 Prettier 风格的格式化器,作为一组 FormatRule 跑在同一个引擎里。它替代 prettier,由 ttsc format 应用。本页讲格式化与 lint 的关系、配置模型、宽度感知重排引擎、以及 print_*.go 的角色。源码 packages/lint/linthost/rules_format_*.go、print_*.go、format.go、config_format.go。
格式化即一组 FormatRule
格式化器不是独立子系统,而是一批实现 FormatRule 接口(IsFormat() bool)的规则(见 规则与注册表)。这让它复用引擎的遍历、Context、autofix 机制:
ttsc fix:应用 lint 类 + FormatRule 类的全部 edit。ttsc format:过滤到 FormatRule finding,只应用格式化重写,写回磁盘,不类型检查。
引擎用 Finding.IsFormat 标志路由(engine.go:122),isFormatRule(engine.go:74)判定一条规则是否 opt-in 格式化类别。
配置模型:单独的 format block
README(packages/lint/README.md)强调:格式化只经 lint.config.ts 的 format block 配置,不经 rules map。放进 rules 的 format/* id 被忽略。
关键语义:
formatblock 存在(哪怕空format: {})就启用 always-on 格式化规则(Prettier 默认值),让ttsc format重写源。severity(默认"off"):check 时格式化诊断的级别,不影响ttsc format。ttsc check默认不因格式化失败,除非 opt-informat.severity。sortImports是 opt-in:只在你设了它才生效。其他键 block 一出现就生效。
config_format.go 与 format_editor_settings.go 解析这个 block,format.go 是格式化入口。
配置键到行为的映射
还有一批 keyless 布局行为随 format block 出现而生效:语句拆分(rules_format_statement_split.go)、缩进、空白规范化(rules_format_whitespace.go)、子句合并(rules_format_clause_join.go)、声明头重排(rules_format_declaration_header.go)、三元 nullish 括号(rules_format_ternary_nullish_parens.go)、leading-semicolon 合并、参数属性拆分(rules_format_parameter_properties.go)。
宽度感知重排引擎(print_*.go)
格式化的难点是列感知换行:对象/数组字面量、call/new 参数、具名 import/export 子句在 flat 形式超出 printWidth 时跨行折断、带尾逗号。这套逻辑在 print_*.go 一组文件里:
按种类拆分的打印文件:print_nodes_array.go、print_nodes_call.go、print_nodes_function.go、print_nodes_imports.go、print_nodes_list.go、print_nodes_object.go、print_nodes_ternary.go。display_width.go 算显示宽度(全角字符等)。这是一个从 Prettier 的 doc 模型移植来的、独立于 @ttsc/factory printer 的实现(factory 是给代码生成的零依赖 printer,lint 这套是给源码格式化的)。
与 @ttsc/factory printer 的关系
两者都是"宽度感知 printer",但用途不同、代码独立:
它们共享"超 printWidth 就 break + 尾逗号"的理念,但不共享代码。见 @ttsc/factory 模块。
测试纪律
格式化器有一类特殊的测试陷阱(.codex/skills/development/SKILL.md 记录的真实事故):只喂规则它自己的规范输出、断言不变,证明的是幂等性而非正确性。一批 formatter 过度匹配就是这么 ship 的——predicate hug 或 break 了 Prettier 不动的形状,但每个测试都喂已正确的例子,什么都没触发。所以每条格式化规则需要:
- 转换方向:mangled/未格式化输入 → 规范输出(输入≠输出)。
- 负向 twin:相邻一个属性之差、必须不动作的例子。
- 边界:空、单元素、恰好宽度上限、最深嵌套。
- oracle 派生期望:期望取自 Prettier 3.8.3 权威输出,不是当前代码碰巧 emit 的。
测试在 packages/lint/test/format/(10.8k LOC)与 4.6k LOC)。test/printer/(
不变量
- 格式化只经
formatblock 配置,rules里的format/*被忽略。 ttsc format写回磁盘无视severity;ttsc check默认不因格式化失败。- format block 存在即启用 always-on 规则(Prettier 默认值)。
sortImports必须显式设才生效。