Skip to content

ccaprani/clat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

clat — Opinionated LaTeX Tidy

PyPI Python versions Docs License: MIT

Colin's LaTeX Tidy — somewhere between a clang and a splat.
📖 Documentation

A configurable LaTeX source formatter with opinions. Every rule has a weight; you set a threshold. A weight of 0 disables a rule. What happens otherwise depends on the combination:

  • clang -- weight >= threshold and fixable: auto-fixed
  • clunk -- weight >= threshold and not fixable: needs your attention
  • splat -- 0 < weight < threshold: advisory, take it or leave it
  • off -- weight <= 0: disabled

Why "clat"?

Prosaically, clat is Colin's LaTeX Tool. But the name also has a bit of printer's noise in it.

In the "Aeolus" episode of Ulysses -- "How a great daily organ is turned out" -- Bloom moves through the newspaper office amid the machinery of print: clanking, rhythmic, three-four time; thump, thump, thump. My great-great-grandfather, a Dublin printer, is mentioned there too, though some editions mangle Caprani as Cuprani.

So clat is meant to sound a little like that composing-room racket: a small, opinionated machine for turning untidy copy into clean type.

How clat compares

The usual LaTeX formatting tools — latexindent, tex-fmt, prettier-plugin-latex — are excellent at indentation and line wrapping. They normalise whitespace, align tables, and keep columns tidy.

clat operates at a different layer. It doesn't touch indentation. Instead, it applies content-aware style rules that those tools don't cover:

What you need Tool
Indentation, line wrapping, alignment latexindent, tex-fmt
Style conventions and writing hygiene clat

For example, clat can:

  • Merge stray \labels onto their \section lines
  • Split prose to one sentence per line
  • Replace {\bf text} with \textbf{text}
  • Configure inline and display math delimiter preferences separately
  • Ensure ~ before \ref, \cite, and similar commands
  • Replace ... with \dots
  • Normalise number-unit spacing (100\,kN)
  • Keep table row endings (\\) with row content and horizontal rules on their own lines
  • Detect hardcoded references (Figure 3 instead of \cref{...})
  • Add trailing punctuation to display equations
  • Convert superscript ordinals (1\textsuperscript{st}1st)
  • And more

None of these are available in latexindent, tex-fmt, or prettier-plugin-latex. clat is complementary — you can (and should) run both. A typical pipeline:

tex-fmt main.tex    # indentation, line wrapping
clat main.tex       # style rules

The weight/threshold system (clang/clunk/splat/off) also gives you granular control over which rules auto-fix, which flag for manual review, which are just advisory noise, and which are disabled — something the existing tools don't offer.

Install

pip install clat-tidy

The command is clat. (The distribution is published as clat-tidy because clat was already taken on PyPI; everything you type is still clat.)

For local development with conda:

conda env create -f environment.yml
conda activate clat

Or install into an existing environment:

pip install -e '.[docs]'

Requires Python >= 3.8. On Python < 3.11, tomli is installed automatically.

Quick start

# Format files in place
clat main.tex appendix.tex

# Format a multi-file document by following LaTeX inputs/includes
clat -r main.tex

# Dry run (report without fixing)
clat --check main.tex

# Dry run a multi-file document
clat --check -r main.tex

# List all rules
clat list

# Override threshold for one run
clat --threshold 3 main.tex

# Limit formatting to a single pass instead of fixed-point sweeps
clat --max-iter 1 main.tex

Rules

Run clat list to see all rules with their numbers, weights, and current category.

# Rule Default Fixable Description
1 labels_inline 8 yes Merge \label onto the same line as \section
2 decorative_comments 6 yes Strip decorative comment separators (%%=== etc.)
3 heading_spacing 7 yes Two blank lines before headings, none after
4 equation_separators 7 yes Insert % lines around display-math environments
5 equation_punctuation 6 yes Add trailing comma or period to display equations
6 float_indentation 5 yes Tab-indent content inside figure/table/list environments
7 one_sentence_per_line 8 yes Split sentences onto individual lines
8 math_delimiters_inline 5 yes Replace \(...\) with $...$
9 math_delimiters_display 0 yes Replace \[...\] with $$...$$
10 math_delimiters_equation 0 yes Replace \[...\] or $$...$$ with equation
11 tilde_before_refs 7 yes Ensure ~ before \ref, \cite etc.
12 number_unit_spacing 6 yes Normalise number-unit spacing (100\,kN)
13 old_font_commands 5 yes Replace {\bf text} with \textbf{text} etc.
14 ellipsis 4 yes Replace ... with \dots
15 ordinal_suffixes 8 yes Convert superscript ordinals to plain text (1st, 2nd)
16 table_line_endings 7 yes Keep table \\ on row lines and rules on own lines
17 abbreviation_spacing 7 yes Force interword space after e.g., i.e., et al.
18 long_file 3 no Warn if file exceeds 2000 lines
19 hardcoded_refs 6 no Detect Figure 3 instead of \cref{...}
20 manual_sizing 3 no Detect \big, \Big etc.
21 float_after_heading 4 no Detect float placed directly after a heading

Configuration

Config is read from .clat.toml in the project directory, falling back to ~/.config/clat/config.toml.

Create a config file

clat set --init

This generates .clat.toml with all rules and their default weights:

threshold = 5

# Contents of these environments are left untouched by every rule.
protected_environments = ["tikzpicture", "pgfpicture", "axis", "tikzcd"]
# Rule ids listed here still run inside protected environments.
unprotected_rules = []

[weights]
labels_inline         =  8  # Merge \label onto the same line as \section (fixable)
decorative_comments   =  6  # Strip decorative comment separators (fixable)
heading_spacing       =  7  # Two blank lines before headings, none after (fixable)
# ... all 21 rules listed

Protected environments

clat masks picture/plot environments — TikZ, pgfplots, tikz-cd by default — out before any rule runs and restores them verbatim, so their contents are never reformatted. Adjust protected_environments to add or remove environments (set it to [] to disable), or list a rule id in unprotected_rules to let just that rule run inside them. See the configuration docs.

Edit the file directly, or use the CLI:

Set threshold

clat set --threshold 8

Set a rule weight

# Prefer rule ids for scripts/configuration
clat set ellipsis 9                  # set ellipsis to weight 9
clat set hardcoded_refs 2            # demote hardcoded refs to a splat
clat set math_delimiters_display 5   # enable display math delimiter conversion
clat set math_delimiters_equation 5  # convert display delimiters to equation

# List numbers are also accepted for interactive use
clat set 14 9

Reset to defaults

clat set --reset

One-shot overrides

Override threshold for a single run without modifying the config:

clat --threshold 3 main.tex

CLI reference

clat <files...>              Format .tex files in place
clat -r <root.tex...>        Recursively format LaTeX inputs/includes
clat --recursive <root.tex>  Long form of -r
clat --check <files...>      Dry run: report issues without fixing
clat --check -r <root.tex>   Dry run a multi-file document
clat -o out.tex in.tex       Write to a different file
clat --threshold N <files>   Override threshold for this run
clat --max-iter N <files>    Maximum fixable-rule sweeps (default 5)

clat list                    List all rules with weights and categories
clat list --config path      Use a specific config file

clat set <rule-id|rule#> <weight>
                                Set a rule weight in .clat.toml (0 disables)
clat set --threshold N       Set the threshold in .clat.toml
clat set --init              Create .clat.toml with defaults
clat set --reset             Restore .clat.toml to defaults
clat set --config path       Target a specific config file

clat --version               Show version

Multi-file documents

Use -r or --recursive when a root .tex file assembles a document from other source files:

clat -r main.tex

Recursive mode follows \input, \include, \subfile, \import, \subimport, \includefrom, and \subincludefrom commands. Paths are resolved relative to the file containing the command, .tex is appended when no extension is given, duplicate files are skipped, and missing .tex inputs are reported through the normal formatter output.

How it works

Each rule is either fixable (clat can rewrite the source) or unfixable (clat can only detect the issue). clat applies enabled fixable rules repeatedly until a full sweep makes no text changes, up to --max-iter sweeps (default 5). Then detect-only rules run once on the final text. At runtime, each rule's weight is compared to the threshold:

weight >= threshold + fixable     =>  clang (auto-fixed)
weight >= threshold + NOT fixable =>  clunk (needs manual fix)
0 < weight < threshold            =>  splat (advisory)
weight <= 0                       =>  off (disabled)

Fixable rules below threshold are still applied (the text is still fixed), but reported as splats rather than clangs. Set a rule's weight to 0 to disable it entirely. Use --max-iter 1 for single-pass behaviour.

Development

pip install -e .
python -m pytest tests/ -v

License

MIT

About

Colin's LaTeX Tidy — opinionated LaTeX source formatter

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors