命令行 LaTeX 代码格式化工具,提供代码美化与规范化能力。
核心原则:绝不因格式化导致原本能编译的代码无法编译,绝不因格式化导致原本的样式在格式化后出现差异。
遇到不确定的情况,宁保留原样,也不随意修改。
环境要求:C++17、CMake 3.16+。
mkdir build && cd build
cmake ..
make -j$(nproc)生成的可执行文件为 build/latex-fmt。
# 方式一:在项目根目录执行
cmake --install build # 安装到默认位置(/usr/local)
cmake --install build --prefix /usr/local # 显式指定安装路径
# 方式二:在 build 目录内执行
cd build && make install # 使用 Makefile 安装
# 安装到用户本地目录(无需 root 权限)
cmake --install build --prefix ~/.local
make install DESTDIR=~/.local # 或从 build 目录执行# 从标准输入读取,格式化到标准输出
latex-fmt < input.tex > output.tex
# 格式化文件并输出到标准输出
latex-fmt paper.tex
# 原地格式化
latex-fmt -i paper.tex
# 输出到指定文件
latex-fmt -o out.tex paper.tex
# Markdown 转 LaTeX 并格式化
latex-fmt --md < input.md > output.tex以下格式化规则可通过命令行参数控制开启或关闭。
| 规则 | 说明 |
|---|---|
| 数学定界符统一 | \(...\) → $...$,\[...\] → $$...$$(行内/行间公式样式可通过 --inline-math-style / --display-math-style 配置) |
| 花括号补全 | \frac12 → \frac{1}{2}、\sqrt3 → \sqrt{3}(仅限已知命令);数学模式上下标简写 x_a → x_{a}、x^2 → x^{2} |
| 去冗余空白 | 文本模式多个空格压缩为 1 个;数学模式保留空格 |
| 缩进与换行 | 按 \begin/\end 层级自动缩进(默认每层 2 空格) |
| 表格与公式对齐 | 表格(tabular/tblr 等)和数学环境(align/matrix/cases)中按 & 纵向对齐单元格,\hline 等分隔线独立成行 |
| CJK-English 间距 | 中文与英文/数字之间自动添加空格 |
| 注释规范化 | %comment → % comment |
| 行尾空格清除 | 每行末尾空格全部删除 |
| 空白行处理 | 纯空白行变为空行;连续多空行压缩为 1 个 |
| 行间公式独立 | $$...$$ 自动独立成行并缩进(输出样式可通过 --display-math-style 切换为 \[...\] / \begin{equation} / \begin{equation*}) |
| 错误恢复 | 不匹配的花括号、缺失 \end 时不会崩溃,尽力恢复 |
| 方括号智能处理 | 数学模式中 [/] 视为普通字符;仅在命令/环境可选参数位置检查配对 |
| 长行警告与自动换行 | --max-line-width=N 警告超长行;--wrap / --wrap-paragraphs 自动折行 |
| 语法检查 | --syntax-check 检查环境/花括号/数学定界符配对,报告位置 |
| 语法修复 | --syntax-fix 基于语法检查结果自动补全缺失的 } ] $ $$ \end 等 |
| 删除公式编号 | --remove-tags 删除 \tag{...} 和 \tag*{...} 命令 |
| Markdown 转换 | --md 将 Markdown 输入转换为 LaTeX 代码并格式化 |
| 列表项换行 | --item-newline 将 \item 内容放在独立行,--item-newline-threshold=N 设置触发换行的最小字符数 |
所有规则均可通过命令行或配置文件单独控制
| 参数 | 默认值 | 说明 |
|---|---|---|
--indent-width=N |
2 | 缩进宽度(空格数) |
--no-cjk-spacing / --cjk-spacing |
开启 | CJK 与 ASCII 字符间自动插入空格 |
--no-brace-completion / --brace-completion |
开启 | 单字符参数自动补全花括号 |
--no-comment-normalize / --comment-normalize |
开启 | %comment → % comment |
--no-blank-line-compress / --blank-line-compress |
开启 | 连续多空行压缩为 1 个 |
--keep-trailing-spaces / --remove-trailing-spaces |
删除 | 是否保留行尾空格 |
--no-display-math-format / --display-math-format |
开启 | $$...$$ 独立成行 |
--no-math-unify / --math-unify |
开启 | 是否统一数学公式的定界符 |
--no-math-trim / --math-trim |
开启 | 是否修剪数学环境中的多余空格($ 定界符与内容之间、{} 内前后空格) |
--display-math-style=S |
dollar |
行间公式输出样式:dollar($$...$$)、bracket(\[...\])、equation、equation* |
--inline-math-style=S |
dollar |
行内公式输出样式:dollar($...$)、bracket(\(...\)) |
--max-line-width=N |
0(关闭) | 当 N > 0 时,超长行触发警告 |
--wrap |
关闭 | 配合 --max-line-width 自动折行 |
--wrap-paragraphs |
关闭 | 纯文本段落长行自动折行 |
--syntax-check |
关闭 | 格式化前检查语法错误(环境/括号/数学定界符配对) |
--syntax-fix |
关闭 | 自动补全缺失的 } ] $ \end 等定界符 |
--remove-tags |
关闭 | 删除公式中的 \tag{...} / \tag*{...} 命令 |
--context-chars=N |
10 | --syntax-check 错误信息中显示的前后文字数 |
--item-newline |
关闭 | 将 \item 内容放在独立行并缩进 |
--item-newline-threshold=N |
0 | \item 内容超过 N 个字符时才换行(0 = 始终换行) |
# 检查是否需要格式化(CI 用,有变更时退出码为 1)
latex-fmt --check paper.tex
# 显示 unified diff 差异
latex-fmt --diff paper.tex# 缩进宽度设为 4
latex-fmt --indent-width=4 paper.tex
# 关闭 CJK-ASCII 自动空格
latex-fmt --no-cjk-spacing paper.tex
# 长行自动折行(行宽 80)
latex-fmt --max-line-width=80 --wrap-paragraphs paper.tex
# 行间公式输出为 \begin{equation}...\end{equation}
latex-fmt --display-math-style=equation paper.tex
# 关闭数学公式空格修剪(保留 $...$ 和 {} 内空格)
latex-fmt --no-math-trim paper.tex
# 组合多项参数
latex-fmt --indent-width=4 --no-cjk-spacing --quiet paper.tex# 原地格式化目录下所有 .tex 文件
latex-fmt -r -i project/
# 递归检查目录
latex-fmt -r --check project/
# 递归显示 diff
latex-fmt -r --diff project/通过 --md 选项,可将 Markdown 文本转换为 LaTeX 代码并自动格式化:
# 从 Markdown 文件转换
latex-fmt --md input.md -o output.tex
# 从标准输入读取 Markdown
cat README.md | latex-fmt --md > readme.tex
# 配合其他格式化参数使用
latex-fmt --md --indent-width=4 notes.md当前支持的 Markdown 语法:
| 语法 | Markdown | LaTeX |
|---|---|---|
| 标题 | # Title |
\section{Title} |
| 加粗 | **bold** |
\textbf{bold} |
| 斜体 | *italic* |
\textit{italic} |
| 行内代码 | `code` |
\texttt{code} |
| 代码块 | ```...``` |
\begin{verbatim}...\end{verbatim} |
| 链接 | [text](url) |
\href{url}{text} |
| 无序列表 | - item |
\begin{itemize} \item item \end{itemize} |
| 有序列表 | 1. item |
\begin{enumerate} \item item \end{enumerate} |
| 引用 | > quote |
\begin{quote}quote\end{quote} |
| 行内公式 | $E=mc^2$ |
原样保留(不转义内部字符) |
| 行间公式 | $$...$$ |
原样保留(内容不做转义) |
注意:公式中的
&、\\、_、#等 LaTeX 特殊字符不会被转义,$...$和$$...$$支持跨多行。$$单独成行时视为行间公式,前方无空行时视为内联闭合定界符。
--syntax-check 在格式化前对文档进行基本语法校验,--syntax-fix 能自动修复检测到的缺失定界符问题。
# 仅检查语法(不格式化),有错误时退出码为 1
latex-fmt --syntax-check paper.tex
# 自动修复缺失的定界符并格式化
latex-fmt --syntax-fix paper.tex
# 调整错误上下文显示字符数(默认 10)
latex-fmt --syntax-check --context-chars=20 paper.tex检测和修复覆盖范围:
| 问题类型 | 示例 | --syntax-check |
--syntax-fix |
|---|---|---|---|
| 未关闭环境 | \begin{document} 缺少 \end{document} |
报错 | 插入 \end{document} |
| 未关闭行内公式 | $x+y 缺少闭合 $ |
报错 | 插入 $ |
| 未关闭行间公式 | $$x+y 缺少闭合 $$ |
报错 | 插入 $$ |
| 未关闭花括号 | \textbf{Bold 缺少 } |
报错 | 插入 } |
| 未关闭方括号(命令可选参数) | \sqrt[3{2} 缺少 ] |
报错 | 智能插入 ] |
| 多余闭合定界符 | }text 多余 } |
报错 | 插入 { 配对 |
| 环境名缺括号 | \begin{equation 缺 } |
报错 | 自动识别 |
关于方括号:
[/]仅在紧跟在已注册命令或环境名后时视为可选参数分隔符。在数学模式($...$/$$...$$)和普通文本中,[和]是普通的字面字符,不需要成对出现。例如张量记号\delta^{[a_1}_{a_1}中的[不会触发语法错误。
注意:
--syntax-check仅报告错误不修改文件。--syntax-fix会修改源文件内容后再格式化。两者均默认关闭,适用于信任文档能正常编译的场景。
使用 --remove-tags 可以删除从其他文档粘贴过来的公式中带有的 \tag{...} 编号,避免原文编号与新文档冲突:
# 删除所有 \tag{...} 和 \tag*{...} 命令
latex-fmt --remove-tags paper.tex支持 \tag{} 和 \tag*{} 两种形式,适用于行间公式($$...$$)和各数学环境(equation、align 等)。
# 抑制警告输出
latex-fmt --quiet paper.tex
# 指定自定义配置文件
latex-fmt --config-file=/path/to/.latexfmtrc paper.texlatex-fmt 支持 .latexfmtrc 配置文件,按以下优先级查找(后找到的覆盖先找到的):
~/.latexfmtrc(全局配置)./.latexfmtrc(项目目录配置)--config-file=<path>指定的文件(最高优先级)
命令行参数始终覆盖配置文件中的设置。
配置文件格式为 key = value,# 开头的行为注释:
# .latexfmtrc 示例
indent_width = 4
max_line_width = 80
cjk_spacing = false
wrap_paragraphs = true
# 布尔值支持: true / false / 1 / 0 / yes / no / on / off
brace_completion = true
comment_normalize = true
# 行间公式输出样式: dollar / bracket / equation / equation*
display_math_style = bracket
# 行内公式输出样式: dollar / bracket
inline_math_style = dollar
# 其他格式化选项
blank_line_compress = true
trailing_whitespace_remove = true
display_math_format = true
math_delimiter_unify = true
math_trim_spaces = true
wrap = false
remove_tags = false
context_chars = 10
# 列表项换行: true/false; 换行阈值: 字符数 (0 = 始终换行)
item_newline = false
item_newline_threshold = 0latex-fmt/
├── src/
│ ├── core/ # 基础类型与配置
│ │ ├── config.h # FormatConfig 配置结构体
│ │ ├── registry.h # 命令/环境签名注册表
│ │ ├── char_category.h# 字符分类工具
│ │ ├── unicode_width.h# UTF-8 解码与字符宽度
│ │ ├── source_location.h
│ │ ├── syntax_check.h # 语法检查器
│ │ └── syntax_fix.h # 语法修复器
│ ├── parse/ # 词法与语法分析
│ │ ├── ast.h # AST 节点定义
│ │ ├── lexer.h # 词法分析器 → Token 流
│ │ └── parser.h # 递归下降解析器 → AST
│ ├── format/ # 格式化引擎
│ │ ├── visitor.h # AST Visitor,生成格式化输出
│ │ └── math_aligner.h # 表格与数学环境列对齐算法
│ ├── md/ # Markdown 转 LaTeX 转换
│ │ └── md_converter.h # Markdown → LaTeX 转换器
│ ├── utils/ # CLI 工具函数
│ │ ├── io.h # 文件读写
│ │ ├── diff.h # unified diff 生成
│ │ └── wrap.h # 行自动换行
│ └── main.cpp # CLI 入口
├── tests/
│ └── snapshots/ # 快照测试(.tex 输入 + .expected.tex 预期)
├── scripts/
│ └── setup.sh # 下载并安装 Catch2
├── external/ # 第三方依赖(gitignore,由脚本生成)
└── CMakeLists.txt
# 首次运行需安装 Catch2
./scripts/setup.sh
# 构建并运行测试
cd build
cmake .. -DBUILD_TESTS=ON
make -j$(nproc)
ctest- 语言:C++17
- 构建:CMake 3.16+
- 测试:Catch2 v3(可选)
- 依赖:无外部运行时依赖
以下列出已注册的所有命令和环境。未列出的命令不会被修改,原样保留。
\textbf \textit \emph \texttt \textsf \textrm \textsc \textup \textnormal — 各 1 个必选参数,支持花括号补全。
| 命令 | 参数格式 | 说明 |
|---|---|---|
\frac |
{2} |
分数 |
\sqrt |
[1]{1} |
根号(可选根指数) |
\binom |
{2} |
二项式系数 |
\left \right — 动态尺寸分隔符,其后接定界符(如 ( [ \{ \langle 等),需成对出现。
| 命令组 | 示例 | 说明 |
|---|---|---|
\bigl \Bigl \biggl \Biggl |
\bigl( ... \bigr) |
左侧定尺寸分隔符 |
\bigr \Bigr \biggr \Biggr |
\Bigl[ ... \Bigr] |
右侧定尺寸分隔符 |
\big \Big \bigg \Bigg |
\big| ... \big| |
中性定尺寸分隔符 |
分隔符可以是:. [ ] \{ \} ( ) \| \langle \rangle \lfloor \rfloor \lceil \rceil。
| 命令 | 参数格式 | 说明 |
|---|---|---|
\stackrel \overset \underset |
{2} |
上下堆叠 |
\substack |
{1} |
多行下标 |
\text \operatorname |
{1} |
数学模式中的文本 |
\tag \tag* |
{1} |
公式编号 |
\notag \nonumber \limits \nolimits \displaylimits \displaystyle \textstyle \scriptstyle \scriptscriptstyle \smash \mathclap \mathrlap \mathllap \dd — 无参数命令。
\DeclareMathOperator {2}、\newcommand \renewcommand \providecommand \newenvironment \renewenvironment(参数按独立 Group 解析)、\ensuremath {1}。
\section \subsection \subsubsection \paragraph \subparagraph \title \author \date — 各 1 个必选参数,支持花括号补全。
\label \ref \pageref \eqref {1}、\cite [1]{1}。
\usepackage [1]{1}、\documentclass [1]{1}。
\caption {1}、\includegraphics [1]{1}。
\item [1](可选方括号参数)。
\maketitle \tableofcontents \newpage \clearpage \centering \raggedright \raggedleft \noindent \indent \newline \linebreak \pagebreak \hfill \vfill \hrulefill \dotfill \smallskip \medskip \bigskip \par \today \appendix \baselineskip。
\captionsetup {1}、\subcaption {1}(支持花括号补全)。
\graphicspath {1}、\DeclareGraphicsExtensions {1}、\includesvg [1]{1}、\color [1]{1}、\textcolor [1]{1}、\colorbox [1]{1}、\pagecolor {1}、\definecolor {3}。
\bm {1}(支持花括号补全)。
\ce {1}(化学式)、\pu {1}(物理单位)。
| 命令 | 参数格式 | 说明 |
|---|---|---|
\abs \norm \qty \order |
{1} |
绝对值 / 范数 / 括号 / 阶 |
\mqty |
{1} |
矩阵 |
\eval \comm \dv \pdv \odv \derivative |
{2} |
求值 / 对易子 / 导数 |
\ket \bra |
{1} |
右矢 / 左矢 |
\braket |
{2} |
内积 |
\set |
{1} |
集合 |
\Bra \Ket |
{1} |
大尺寸矢 |
\dd |
— | 微分算子 |
\xleftrightarrow \xlongequal \xLongrightarrow {1}。
\degree \celsius \micro \perthousand \ohm(无参数)。
\KwIn \KwOut \KwData \KwResult \For \While \If \Else \ElseIf \Return \SetKw \SetAlgoLined \LinesNumbered \BlankLine(无参数)、\SetKwInOut {2}、\tcp {1}。
\setlist {1}、\newlist {3}、\setenumerate \setitemize \setdescription(无参数)。
\lipsum [1](英文盲文)、\zhlipsum [1](中文盲文)。
\part / \part* [1]{1} / {1}、\chapter / \chapter* [1]{1} / {1}、\thanks {1}、\frontmatter \mainmatter \backmatter \listoffigures \listoftables \pagestyle \pagenumbering(无参数)。
| 命令 | 参数格式 | 说明 |
|---|---|---|
\frametitle \framesubtitle |
{1} |
帧标题/副标题 |
\usetheme \usecolortheme \usefonttheme \useinnertheme \useoutertheme |
{1} |
主题设置 |
\titlegraphic \logo |
{1} |
标题图/徽标 |
\institute \alert \structure |
{1} |
单位/高亮/结构色文字(花括号补全) |
\onslide |
{1} |
叠层可见范围 |
\only \uncover \visible \invisible |
{2} |
叠层条件显示 |
\pause \titlepage |
— | 暂停/标题页 |
\insertsectionhead \insertsubsectionhead |
— | 插入当前节/子节标题 |
\hline [1]、\toprule \midrule \bottomrule \cmidrule — 表格横线命令,格式化时自动独立成行。
| 环境 | 对齐策略 | 说明 |
|---|---|---|
align / align* aligned alignedat flalign / flalign* split |
& 交替对齐 |
多行公式对齐 |
gather / gather* gathered equation / equation* multline / multline* |
无 | 居中/单行/长公式 |
pmatrix bmatrix Bmatrix vmatrix Vmatrix matrix smallmatrix |
矩阵等宽列 | 各类矩阵 |
cases dcases |
分支对齐 | 分段函数 |
itemize(无序) enumerate(有序) description(描述)。
verbatim lstlisting minted — 内容不做格式化处理。
\verb|...| 和 \verb*|...| — 内联逐字命令,内容原样保留不做任何格式化处理。\verb* 将空格显示为可见字符。分隔符可选用除 * 外的任意非字母字符(如 |、!、+ 等)。
| 环境 | 对齐策略 | 说明 |
|---|---|---|
tabular tabularx tblr longtblr talltblr |
& 等宽列 + 分隔线独立行 |
表格单元格对齐 |
figure / figure* table / table* subfigure subtable。
algorithm function。
frame columns column block exampleblock alertblock overlayarea overprint。
abstract。
center flushleft flushright quote quotation tikzpicture。
「随君所想,成君所愿」