qfmt — 命令行 Q+ 格式化器(v3,1.6.0-dev)
English summary.
qfmtis the standalone Q+ formatter (python3 stdlib). v3 adds project-level recursion,.qfmt.tomldiscovery (git-root stop,--config/--no-config),--exclude,--jobs,--diff; 306 checks / 29 fixtures. Safe subset: whitespace/layout only, no semantic rewrite; untrusted structure returns rc=2 unchanged and files are never implicitly rewritten; not a complete formatter.
Q+ 1.4.8 引入独立命令行格式化器 qfmt(当前 v3,1.5.0-dev)(tools/qfmt.sh / tools/qfmt.py,python3 标准库、 零第三方依赖)。它是 VS Code 扩展 formatter 规则的忠实移植,供命令行/CI/编辑器无关场景使用。 本页记录用法、规则、前后对比与边界。
用法
tools/qfmt.sh <file.q> # 格式化结果到 stdout(默认只读)
tools/qfmt.sh --write <file.q> # 原地写回(原子替换,显式 opt-in)
tools/qfmt.sh --check <file.q> # 只检查;未格式化时打印文件名,rc=1
tools/qfmt.sh --stdin < in.q # 从 stdin 读、stdout 输出
tools/qfmt.sh --indent 2 <file.q> # 缩进宽度(默认 4)
tools/qfmt.sh --tabs <file.q> # 用 tab 缩进
退出码:0 成功;--check 有差异 1;用法/IO/结构错误 2。无文件参数时读 stdin。
前后对比(静态示例)
格式化前:
use std.io;
fn main() -> i32{
let x:i32=1+2;
if x>0{println_int(x)}
return 0
}
tools/qfmt.sh --stdin 输出(规范化空白/缩进/内联块展开):
use std.io;
fn main() -> i32 {
let x: i32 = 1 + 2;
if x > 0 {
println_int(x)
}
return 0
}
该示例为静态对比(Playground 中不要求执行 qfmt;格式化后代码本身可编译运行)。
规则(安全子集)
- 按
{ }嵌套缩进(默认 4 空格,可 2/8/tab);行首}回退一级; - 内联块展开(
if c { return 1 }→ 多行);} else {等闭合形式保持; - 空格规范化:
,/;后一个空格,运算符两侧一个空格,name: Type冒号规则;字符串/注释内部不动; - 空行折叠(连续空行→1,块首尾空行删除),文件末尾恰好 1 个换行,删除行尾空白;
- CRLF 输入保持 CRLF;幂等:
fmt(fmt(x)) == fmt(x); - 不做:完整语法解析/语义分析、长行折行、注释对齐、import 排序、结构重写、配置文件; 输入 >2 MiB 拒绝(rc=2);结构不可信(括号/字符串/块注释未闭合)→ 原样输出、rc=2、不写文件。
验证
bash tools/test_qfmt.sh → 306 checks / 29 fixtures(v3:项目级递归/配置发现/--exclude/--jobs/--diff):20 个 valid fixtures 格式化后编译+运行 输出/rc 不变、幂等、--write/--check、语法错误原样+rc2、CRLF/空白属性、与扩展 formatter 对齐 (node 可用时;15/15 扩展用例、40/40 真实文件逐字节一致)。qfmt 保留 1e-7 指数写法(扩展 formatter 曾误插空格导致编译失败——qfmt 修复该安全问题并保持格式化后可编译)。
边界(不得宣称全语法覆盖)
- 只保证安全子集与已测 fixtures;不覆盖全部 Q+ 语法/所有风格选项;
- 结构校验是保守检查而非完整 parser;不处理长行折行/对齐/注释重排;无项目级配置;
- 不自动接入 VS Code/LSP(扩展内 formatter 行为不变);不修改官方文件。
相关:docs/FORMATTER.md;发布候选 NOT PUBLISHED。