I have some strong opinions about how Markdown should look. Liberal line breaks everywhere. ATX headers with exactly one space after the #. Consistent list indentation using tabs. Tables that are properly aligned. And on and on. So I made Markdown Fixup (md-fixup).

Building My Own Tool

I just wanted a tool to fix my own Markdown files up to match these preferences. But then I thought, maybe someone else out there has the same opinions, or at least could be swayed to think they were good ideas? So I decided to make it available to the world, just in case anyone’s tastes matched mine.

md-fixup is a comprehensive markdown linter and formatter that performs 27 different normalization and formatting rules. It solves some common issues you might run into with “it works in X.app but not in Y” by standardizing on a consistent set of rules.

I considered building this functionality directly into my Apex project, but I determined it was feature bloat and made more sense as a standalone tool. Keeping it separate means it can be used independently, works great in pipelines, and doesn’t add complexity to Apex itself. If you want to use it with Apex, you can always pipe it in (the Unix philosophy of doing one thing well).

Why Not Use Existing Tools?

There are a few good markdown linters available that can be configured to use similar rules — this one is just specific to my own preferences and easy to run. If your preferences happen to align with mine, you might find it useful.

Flexible Usage

The tool takes STDIN and can be used in a pipeline, and can output to STDOUT or overwrite files. Here’s a quick example:

# Process a file and output to stdout
md-fixup file.md

# Overwrite files in place
md-fixup --overwrite file.md

# Use in a pipeline
find . -name "*.md" | md-fixup --width 100

All linters can be enabled or disabled on the command line, via project config files, or via global config (in that order of precedence). So you can customize it to match your exact needs, even if they don’t perfectly align with my defaults.

Configuration

You can skip specific rules if you don’t want them:

# Skip wrapping and end-of-file newline rules
md-fixup --skip wrap,end-newline file.md

# Or use a config file
md-fixup --init-config

The config file lives at ~/.config/md-fixup/config.yml and lets you set defaults for width, overwrite behavior, and which rules to skip.

VS Code Extension

There’s a start of a VS Code extension in the repo, but I haven’t fully developed that yet. It’s on the roadmap, but for now the command-line tool works great.

Installation

You can install it with Homebrew:

brew tap ttscoff/thelab
brew install md-fixup

The release action is generating macOS and Linux binaries (x86, arm) so you don’t need any external tools to install via Homebrew.

You can also visit the GitHub repo for details on installing the Python version or building the Rust version from source.

All The Fixes

Here’s what md-fixup does:

  • Normalizes line endings to Unix
  • Trims trailing whitespace (preserves exactly 2 spaces for line breaks)
  • Collapses multiple blank lines (max 1 consecutive, except in code blocks)
  • Normalizes headline spacing (exactly 1 space after #)
  • Ensures blank line after headline
  • Ensures blank line before/after code block
  • Ensures blank line before/after list
  • Ensures blank line before/after horizontal rule
  • Converts list indentation spaces to tabs consistently
  • Normalizes list marker spacing
  • Wraps text at specified width (preserving links, code spans, fenced blocks)
  • Ensures exactly one blank line at end of file
  • Normalizes IAL (Inline Attribute List) spacing for both Kramdown and Pandoc styles
  • Normalizes fenced code block language identifier spacing
  • Normalizes reference-style link definition spacing
  • Normalizes task list checkbox (lowercase x)
  • Normalizes blockquote spacing
  • Normalizes $$ display and $ inline math block spacing (handles multi-line, preserves currency)
  • Normalizes table formatting (aligns columns, handles relaxed and headerless tables)
  • Normalizes emoji names (spellcheck and correct typos using fuzzy matching)
  • Normalizes typography (curly quotes to straight, en/em dashes, ellipses, guillemets)
  • Normalizes bold/italic markers (bold: always __, italic: always *)
  • Normalizes list markers (renumber ordered lists, standardize bullet markers by level)
  • Resets ordered lists to start at 1 (if disabled, preserves starting number)

Feedback Welcome

Feedback and code contributions are welcome! If you have opinions about Markdown formatting that differ from mine, I’d love to hear about them. And if you want to help build out the VS Code extension or add new features, pull requests are always appreciated.

See details on GitHub and take it for a spin!