Skip to main content

lastlint: LaTeX strict linter

A linter for Evan Chen's LaTeX style guide.

Usage

Pre-commit hook

Add to your .pre-commit-config.yaml:

repos:
  - repo: https://github.com/cjquines/lastlint
    rev: v0.6.0 # x-release-please-version
    hooks:
      - id: lastlint-fix

To only report errors rather than auto-fixing them, use the lastlint hook id.

Standalone

Install from PyPI. Using uv (recommended):

uv tool install lastlint
lastlint path/to/file.tex another.tex

To run without installing: uvx lastlint path/to/file.tex. With pip: pip install lastlint.

Multiple files are accepted, and glob patterns are expanded.

Pass --fix to auto-fix supported rules in place.

Pass --ignore E001,E013 to skip both reporting and fixing.

On a terminal, output is grouped per file and colorized; when piped or redirected it uses a plain file:line:col: EXXX: message format parseable by editors. Force either mode with --color always or --color never (also honors NO_COLOR and FORCE_COLOR).

Editor

An optional language server exposes lastlint to compatible text editors. Install it with the lsp extra:

uv tool install 'lastlint[lsp]'
# or: pip install 'lastlint[lsp]'

The server lastlint-lsp, over stdio, publishes diagnostics and implements formatting, so format-on-save will auto-fix LaTeX files. Pass ignored rules through initializationOptions, e.g. {"ignore": ["E001", "E013"]}.

The setups below assume lastlint-lsp is globally installed. To skip a global install, replace the command with uvx --from 'lastlint[lsp]' lastlint-lsp.

Neovim

0.11+, native vim.lsp:

vim.lsp.config('lastlint', {
  cmd = { 'lastlint-lsp' },
  filetypes = { 'tex', 'plaintex' },
  root_markers = { '.git' },
})
vim.lsp.enable('lastlint')
Sublime Text

The LSP package, in LSP.sublime-settings:

{
  "clients": {
    "lastlint": {
      "enabled": true,
      "command": ["lastlint-lsp"],
      "selector": "text.tex.latex"
    }
  }
}
Emacs

Eglot (built in since 29) — then run M-x eglot in a TeX buffer:

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '((latex-mode LaTeX-mode) . ("lastlint-lsp"))))

Suppression

Add an inline comment to skip rules on a single line:

This text has "intentional" quotes.  % lastlint: disable=E002

Rules

Fixable rules are marked with 🔧.

ID Rule from style guide
E001 Line length must be ≤ 120
E002 🔧 No literal "
E003 🔧 Operators (\sin etc.) must be commands
E004 🔧 No \ldots, \cdots, or ...
E005 🔧 No grammatical punctuation inside inline math
E006 🔧 No extraneous space before punctuation
E007 Spacing around = must be symmetric
E009 🔧 No bad math operators (| etc.)
E010 🔧 No $$ ... $$
E011 No adjacent \[ ... \] blocks
E012 \begin{align*} / \end{align*} must have own line
E013 🔧 Env contents must be indented
E014 🔧 No trailing whitespace
E015 No \\\\ paragraph break
E017 🔧 No : for function signatures

Rules not implemented:

  • Rule 8 (balanced delimiters): hard to lint reliably.
  • Rule 16 (variables in prose): requires natural-language understanding.

E001

E001 skips two kinds of unfixable long lines: those containing a URL (http:// or https://), and fully-commented lines (only whitespace before the %). Lines with a long trailing comment after real content are still flagged.

I recommend SemBr for automatically adding line breaks.

E004

E004 picks the right \dots variant from context (\dotsc after a comma, \dotsb after an operator) and leaves a \cdots unresolved when neither side determines spacing.

E013

E013 checks every env except a small denylist: document, center, quote, and the verbatim envs (asy, tikzpicture, verbatim, lstlisting, …). See NO_INDENT_ENVS in lastlint.py to adjust.

The --fix pads under-indented lines inside indent envs to 2 * depth spaces (already-correctly-indented or deeper lines are left alone), and converts leading tabs to 2 spaces each.

Development

pip install -e .[dev]
pytest

Metadata

Release files for lastlint 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lastlint 0.6.0
File Size Uploaded
lastlint-0.6.0.tar.gz 20.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lastlint 0.6.0
File Interpreter ABI Platform
lastlint-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.2 kB

Release files / lastlint-0.6.0.tar.gz

Download URL lastlint-0.6.0.tar.gz
Size 20.8 kB
Tags Source
SHA-256 checksum
How to use checksums
d43220dac2af36a998a38bf8c1f4d6e65f9902a21411072af2fa3352910ba5a4
BLAKE2b-256 checksum
How to use checksums
77e1a926c3d1d3e6f55ec0b4e032b33ec4468966c84e68aad67be51b64f73da6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / lastlint-0.6.0-py3-none-any.whl

Download URL lastlint-0.6.0-py3-none-any.whl
Size 17.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dd554f2a0edab4ae3407972fda686c962a2ab6f2ff4db71d3f0b26eb60e6779d
BLAKE2b-256 checksum
How to use checksums
27a4e466013eb8ed51dc5407f42b9641fc1d1b90157efd5b08b6700a737f9849
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page