A Markdown formatter that enforces Hong Minhee's Markdown style conventions
197
stars
431
commits
Rust
primary language
Jul 28, 2026
updated
Hongdown is a Markdown formatter that enforces Hong Minhee's Markdown style conventions. The formatter is implemented in Rust using the Comrak library for parsing. It produces consistently formatted Markdown output following a distinctive style used across multiple projects including Fedify, LogTape, and Optique.
winget install HongMinhee.Hongdown
scoop bucket add extras
scoop install hongdown
npm install -g hongdown
mise use -g aqua:dahlia/hongdown
nix run github:dahlia/hongdown
cargo install hongdown
Pre-built binaries for Linux, macOS, and Windows are available on the GitHub Releases page.
# Format a file and print to stdout
hongdown input.md
# Format a file in place
hongdown --write input.md
hongdown -w input.md
# Format multiple files
hongdown -w *.md
# Format all Markdown files in a directory (recursive)
hongdown -w .
hongdown -w docs/
# Check if files are formatted (exit 1 if not)
hongdown --check input.md
hongdown -c input.md
# Show diff of formatting changes
hongdown --diff input.md
hongdown -d input.md
# Read from stdin (use --stdin flag or - as filename)
echo "# Hello" | hongdown --stdin
echo "# Hello" | hongdown -
hongdown --stdin < input.md
hongdown - < input.md
# Custom line width
hongdown --line-width 100 input.md
# Disable word wrapping entirely
hongdown --no-line-width input.md
# Enable MDX mode for non-.mdx input (.mdx files enable it automatically)
hongdown --mdx input.md
Hongdown supports special HTML comment directives to control formatting behavior within documents.
You can disable formatting for specific sections:
<!-- hongdown-disable-file -->
This entire file will not be formatted.
<!-- hongdown-disable-next-line -->
This line preserves its spacing.
The next line will be formatted normally.
<!-- hongdown-disable-next-section -->
Everything here is preserved as-is
until the next heading (h1 or h2).
Next heading
------------
This section will be formatted normally.
<!-- hongdown-disable -->
This section is not formatted.
<!-- hongdown-enable -->
This section is formatted again.
When sentence case is enabled, you can define document-specific proper nouns and common nouns:
<!-- hongdown-proper-nouns: Swift, Go -->
<!-- hongdown-common-nouns: Python -->
# Using Swift And Go For Python Development
This heading will be formatted as: "Using Swift and Go for python development"
<!-- hongdown-proper-nouns: Word1, Word2 --> – Defines proper nouns to
preserve (case-sensitive)<!-- hongdown-common-nouns: Word1, Word2 --> – Overrides built-in proper
nouns by treating them as common nounsThese directives are merged with configuration file settings.
Hongdown supports cascading configuration files from multiple locations. Configuration files are loaded and merged in the following order (lowest to highest priority):
Settings from higher-priority configurations override those from lower-priority ones. This allows you to set global defaults at the user or system level while overriding them for specific projects.
You can also specify a configuration file explicitly with the --config option,
which bypasses the cascading system and uses only that file.
To ignore all system and user configurations and use only your project config:
no_inherit = true
# Your project-specific settings
line_width = 100
When no_inherit = true, only the project config and Hongdown's defaults are
used. This is useful for projects that need strict formatting control
regardless of user preferences.
Below is an example configuration with all available options and their default values:
# File patterns (glob syntax)
include = [] # Files to format (default: none, specify on CLI)
exclude = [] # Files to skip (default: none)
git_aware = true # Respect .gitignore and skip .git directory (default: true)
# Formatting options
line_width = 80 # Maximum line width (min: 8, default: 80); set to false to disable wrapping
math = true # Preserve TeX math ($…$ and $$…$$) verbatim (default: true)
mdx = false # MDX mode: preserve embedded JS/JSX verbatim (default: false)
[heading]
setext_h1 = true # Use === underline for h1 (default: true)
setext_h2 = true # Use --- underline for h2 (default: true)
sentence_case = false # Convert headings to sentence case (default: false)
proper_nouns = [] # Additional proper nouns to preserve (default: [])
common_nouns = [] # Exclude built-in proper nouns (default: [])
anchor_align = 0 # Spaces before anchor {#id}: positive = exact count,
# 0/negative = right-align to line_width+anchor_align (default: 0)
[unordered_list]
unordered_marker = "-" # "-", "*", or "+" (default: "-")
leading_spaces = 1 # Spaces before marker (0–3, default: 1)
trailing_spaces = 2 # Spaces after marker (0–3, default: 2)
indent_width = 4 # Indentation for nested items (min: 1, default: 4)
[ordered_list]
odd_level_marker = "." # "." or ")" at odd nesting levels (default: ".")
even_level_marker = ")" # "." or ")" at even nesting levels (default: ")")
pad = "start" # "start" or "end" for number alignment (default: "start")
indent_width = 4 # Indentation for nested items (min: 1, default: 4)
[code_block]
fence_char = "~" # "~" or "`" (default: "~")
min_fence_length = 4 # Minimum fence length (min: 3, default: 4)
space_after_fence = true # Space between fence and language (default: true)
default_language = "" # Default language for code blocks (default: "")
# External code formatters (see "External code formatters" section)
[code_block.formatters]
# javascript = ["deno", "fmt", "--ext=js", "-"]
[thematic_break]
# Must be valid CommonMark: at least 3 of *, -, or _ (with optional spaces)
style = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
leading_spaces = 3 # Leading spaces (0–3, default: 3)
[punctuation]
curly_double_quotes = true # "text" to "text" (default: true)
curly_single_quotes = true # 'text' to 'text' (default: true)
curly_apostrophes = false # it's to it's (default: false)
ellipsis = true # ... to ... (default: true)
en_dash = false # Disabled by default (use "--" to enable)
em_dash = "--" # -- to --- (default: "--", use false to disable)
Configuration values are validated at parse time. Invalid values will produce descriptive error messages:
$ hongdown --config invalid.toml input.md
Error: failed to parse configuration file: line_width must be at least 8, got 5.
When include patterns are configured, you can run Hongdown without
specifying files:
# Format all files matching include patterns
hongdown --write
# Check all files matching include patterns
hongdown --check
CLI options override configuration file settings:
# Use config file but override line width
hongdown --line-width 100 input.md
# Disable word wrapping (overrides config file)
hongdown --no-line-width input.md
# Use specific config file
hongdown --config /path/to/.hongdown.toml input.md
Hongdown enforces the following conventions:
= or -)###, ####, etc.)Document Title
==============
Section
-------
### Subsection
When sentence_case = true is set in the configuration, Hongdown automatically
converts headings to sentence case using intelligent heuristics:
# Development Commands → Development commands
# Working With JSON APIs → Working with JSON APIs
# Using `MyClass` In Code → Using `MyClass` in code
The converter:
API, HTTP)JSON-RPC)You can add custom proper nouns to preserve:
[heading]
sentence_case = true
proper_nouns = ["MyCompany", "MyProduct", "MyAPI"]
You can also exclude built-in proper nouns by treating them as common nouns. This is useful for words like “Go” which can be either a programming language or a common verb:
[heading]
sentence_case = true
common_nouns = ["Go", "Swift"] # Treat these as common nouns, not proper nouns
Built-in proper nouns include ~450 entries: programming languages (JavaScript, TypeScript, Python, Rust, Go), technologies (GitHub, Docker, React, Node.js), databases (PostgreSQL, MySQL, MongoDB), countries (United States, Republic of Korea), natural languages (English, Korean, Japanese), and more.
You can also use HTML comment directives to define document-specific proper nouns and common nouns. See the “HTML comment directives” section for details.
- (space-hyphen-two spaces)1. format - First item
- Second item
- Nested item
~~~~)~~~~ rust
fn main() {
println!("Hello, world!");
}
~~~~
You can configure external formatters for code blocks in your .hongdown.toml. This allows automatic formatting of embedded code using language-specific tools.
[code_block.formatters]
# Simple format: command as array
javascript = ["deno", "fmt", "--ext=js", "-"]
typescript = ["deno", "fmt", "--ext=ts", "-"]
rust = ["rustfmt"]
# With custom timeout (default is 5 seconds)
[code_block.formatters.python]
command = ["black", "-"]
timeout = 10
Behavior:
javascript matches javascript, not
js)To skip formatting for a specific code block, add hongdown-no-format after the
language identifier:
~~~~ python hongdown-no-format
def hello(): print("Hello, World!")
~~~~
The hongdown-no-format keyword is preserved in the output, ensuring the code
block remains unformatted on subsequent runs.
For WASM builds, use the formatWithCodeFormatter function with a callback:
import { formatWithCodeFormatter } from "@hongdown/wasm";
import * as prettier from "prettier";
const { output, warnings } = await formatWithCodeFormatter(markdown, {
codeFormatter: (language, code) => {
if (language === "javascript" || language === "typescript") {
return prettier.format(code, { parser: "babel" });
}
return null; // Keep original for other languages
},
});
$…$) and display ($$…$$) TeX/LaTeX math is preserved verbatim,
never escaped or punctuation-transformed (like code spans)$, $ command, or
$5 stays literal textmath = false in .hongdown.toml to treat every $ as textThe complexity is $O(n \log n)$.
$$
x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$
MDX documents mix Markdown with JavaScript and JSX. Because the underlying CommonMark parser does not understand those constructs, formatting an MDX document normally corrupts the embedded code. MDX mode (“first, do no harm”) detects the JS/JSX constructs and passes them through verbatim, while the surrounding Markdown prose is still formatted.
Within paragraph text, the constructs Hongdown would otherwise corrupt are preserved verbatim — never escaped or punctuation-transformed:
import/export statements, JSX elements and fragments (<Tabs …>,
<Chart … />, <>…</>), and {…} expressions (including {/* … */}
comments)--mdx or mdx = true in .hongdown.tomlimport { Chart } from "./chart.js";
export const meta = { author: "Hong Minhee" };
<PackageManagerTabs
command={{ npm: "npm add @scope/pkg" }}
/>
A paragraph of prose is still wrapped and punctuation-transformed, while the
JSX and imports above are left exactly as written.
A JSX tag that is already valid inline HTML (for example a single {value}
attribute) is left to the underlying parser, which keeps the tag while still
formatting the element's Markdown children; only tags that break the HTML
grammar (a {{…}} attribute, embedded quotes or spaces, or a multi-line opener)
are protected as a whole.
Some constructs are intentionally left unprotected: anything inside a heading
(an explicit {#id} anchor stays a heading anchor), braces inside inline code
(`{x}`), math ($\frac{1}{2}$), or link/image syntax, an expression
containing a regex literal whose context is ambiguous (a / immediately after
}), and an ESM object whose body contains a blank line.
See the [documentation] for more details.
[documentation]: https://example.com/docs
See STYLE.md for the complete style specification, including the philosophy behind these conventions and detailed formatting rules.
Install the Hongdown extension from the Visual Studio Marketplace or Open VSX Registry, then make it the default formatter for Markdown and MDX:
{
"[markdown]": {
"editor.defaultFormatter": "hongminhee.hongdown"
},
"[mdx]": {
"editor.defaultFormatter": "hongminhee.hongdown"
}
}
The extension uses a bundled WebAssembly build by default. To use a system Hongdown CLI instead:
{
"hongdown.backend": "cli",
"hongdown.cli.path": "hongdown"
}
By default, the extension looks for .hongdown.toml in the workspace root. For
a standalone file outside any workspace, it falls back to the file's containing
directory. Set hongdown.config.path to use a different configuration file.
Add the following to your Zed settings to use Hongdown as the Markdown formatter (contributed by Lee Dogeon):
{
"languages": {
"Markdown": {
"formatter": {
"external": {
"command": "hongdown",
"arguments": ["-"]
}
}
}
}
}
If you use none-ls.nvim (a community-maintained fork of null-ls.nvim), you can register Hongdown as a formatter (requires none-ls-extras.nvim, contributed by Vladimir Rubin):
local hongdown = require('none-ls.formatting.hongdown')
null_ls.register(hongdown)
If you use Helix, you can register Hongdown as a formatter (contributed by Jean Simard):
[[language]]
name = "markdown"
auto-format = true
formatter = { command = "hongdown", args = ["--stdin"] }
Hongdown can also be used as a Rust library:
use hongdown::{format, Options};
let input = "# Hello World\nThis is a paragraph.";
let options = Options::default();
let output = format(input, &options).unwrap();
println!("{}", output);
Hongdown is available as a WebAssembly-based library for JavaScript and TypeScript:
npm install @hongdown/wasm
import { format, formatWithWarnings, loadConfigFromToml } from "@hongdown/wasm";
// Basic usage
const markdown = "# Hello\nWorld";
const formatted = await format(markdown);
// With options
const result = await format(markdown, {
lineWidth: 100,
setextH1: false,
fenceChar: "`",
});
// Get warnings along with formatted output
const { output, warnings } = await formatWithWarnings(markdown);
if (warnings.length > 0) {
for (const warning of warnings) {
console.warn(`Line ${warning.line}: ${warning.message}`);
}
}
// Parse .hongdown.toml for WASM formatting
const { options: configOptions } = await loadConfigFromToml(configToml);
const configured = await format(markdown, configOptions);
External code block formatters configured in code_block.formatters are
reported as warnings because the WASM runtime cannot execute external
commands. Use the CLI for those settings.
The library works in Node.js, Bun, Deno, and web browsers. See the TypeScript type definitions for all available options.
This project uses mise for task management.
After cloning the repository, set up the Git pre-commit hook to automatically run quality checks before each commit:
mise generate git-pre-commit --task=check --write
The following tasks are available:
# Run all quality checks
mise run check
# Individual checks
mise run check:clippy # Run clippy linter
mise run check:fmt # Check code formatting
mise run check:type # Run Rust type checking
mise run check:markdown # Check Markdown formatting
See AGENTS.md for detailed development guidelines including TDD practices, code style conventions, and commit message guidelines.
The name Hongdown is a portmanteau of Hong (from Hong Minhee, the author) and Markdown. It also sounds like the Korean word hongdapda (홍답다), meaning “befitting of Hong” or “Hong-like.”
Distributed under the GPL-3.0-or-later. See LICENSE for more information.
Rust
89.6%
TypeScript
8.7%
A Markdown formatter that enforces Hong Minhee's Markdown style conventions
197
stars
431
commits
Rust
primary language
Jul 28, 2026
updated
Hongdown is a Markdown formatter that enforces Hong Minhee's Markdown style conventions. The formatter is implemented in Rust using the Comrak library for parsing. It produces consistently formatted Markdown output following a distinctive style used across multiple projects including Fedify, LogTape, and Optique.
winget install HongMinhee.Hongdown
scoop bucket add extras
scoop install hongdown
npm install -g hongdown
mise use -g aqua:dahlia/hongdown
nix run github:dahlia/hongdown
cargo install hongdown
Pre-built binaries for Linux, macOS, and Windows are available on the GitHub Releases page.
# Format a file and print to stdout
hongdown input.md
# Format a file in place
hongdown --write input.md
hongdown -w input.md
# Format multiple files
hongdown -w *.md
# Format all Markdown files in a directory (recursive)
hongdown -w .
hongdown -w docs/
# Check if files are formatted (exit 1 if not)
hongdown --check input.md
hongdown -c input.md
# Show diff of formatting changes
hongdown --diff input.md
hongdown -d input.md
# Read from stdin (use --stdin flag or - as filename)
echo "# Hello" | hongdown --stdin
echo "# Hello" | hongdown -
hongdown --stdin < input.md
hongdown - < input.md
# Custom line width
hongdown --line-width 100 input.md
# Disable word wrapping entirely
hongdown --no-line-width input.md
# Enable MDX mode for non-.mdx input (.mdx files enable it automatically)
hongdown --mdx input.md
Hongdown supports special HTML comment directives to control formatting behavior within documents.
You can disable formatting for specific sections:
<!-- hongdown-disable-file -->
This entire file will not be formatted.
<!-- hongdown-disable-next-line -->
This line preserves its spacing.
The next line will be formatted normally.
<!-- hongdown-disable-next-section -->
Everything here is preserved as-is
until the next heading (h1 or h2).
Next heading
------------
This section will be formatted normally.
<!-- hongdown-disable -->
This section is not formatted.
<!-- hongdown-enable -->
This section is formatted again.
When sentence case is enabled, you can define document-specific proper nouns and common nouns:
<!-- hongdown-proper-nouns: Swift, Go -->
<!-- hongdown-common-nouns: Python -->
# Using Swift And Go For Python Development
This heading will be formatted as: "Using Swift and Go for python development"
<!-- hongdown-proper-nouns: Word1, Word2 --> – Defines proper nouns to
preserve (case-sensitive)<!-- hongdown-common-nouns: Word1, Word2 --> – Overrides built-in proper
nouns by treating them as common nounsThese directives are merged with configuration file settings.
Hongdown supports cascading configuration files from multiple locations. Configuration files are loaded and merged in the following order (lowest to highest priority):
Settings from higher-priority configurations override those from lower-priority ones. This allows you to set global defaults at the user or system level while overriding them for specific projects.
You can also specify a configuration file explicitly with the --config option,
which bypasses the cascading system and uses only that file.
To ignore all system and user configurations and use only your project config:
no_inherit = true
# Your project-specific settings
line_width = 100
When no_inherit = true, only the project config and Hongdown's defaults are
used. This is useful for projects that need strict formatting control
regardless of user preferences.
Below is an example configuration with all available options and their default values:
# File patterns (glob syntax)
include = [] # Files to format (default: none, specify on CLI)
exclude = [] # Files to skip (default: none)
git_aware = true # Respect .gitignore and skip .git directory (default: true)
# Formatting options
line_width = 80 # Maximum line width (min: 8, default: 80); set to false to disable wrapping
math = true # Preserve TeX math ($…$ and $$…$$) verbatim (default: true)
mdx = false # MDX mode: preserve embedded JS/JSX verbatim (default: false)
[heading]
setext_h1 = true # Use === underline for h1 (default: true)
setext_h2 = true # Use --- underline for h2 (default: true)
sentence_case = false # Convert headings to sentence case (default: false)
proper_nouns = [] # Additional proper nouns to preserve (default: [])
common_nouns = [] # Exclude built-in proper nouns (default: [])
anchor_align = 0 # Spaces before anchor {#id}: positive = exact count,
# 0/negative = right-align to line_width+anchor_align (default: 0)
[unordered_list]
unordered_marker = "-" # "-", "*", or "+" (default: "-")
leading_spaces = 1 # Spaces before marker (0–3, default: 1)
trailing_spaces = 2 # Spaces after marker (0–3, default: 2)
indent_width = 4 # Indentation for nested items (min: 1, default: 4)
[ordered_list]
odd_level_marker = "." # "." or ")" at odd nesting levels (default: ".")
even_level_marker = ")" # "." or ")" at even nesting levels (default: ")")
pad = "start" # "start" or "end" for number alignment (default: "start")
indent_width = 4 # Indentation for nested items (min: 1, default: 4)
[code_block]
fence_char = "~" # "~" or "`" (default: "~")
min_fence_length = 4 # Minimum fence length (min: 3, default: 4)
space_after_fence = true # Space between fence and language (default: true)
default_language = "" # Default language for code blocks (default: "")
# External code formatters (see "External code formatters" section)
[code_block.formatters]
# javascript = ["deno", "fmt", "--ext=js", "-"]
[thematic_break]
# Must be valid CommonMark: at least 3 of *, -, or _ (with optional spaces)
style = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
leading_spaces = 3 # Leading spaces (0–3, default: 3)
[punctuation]
curly_double_quotes = true # "text" to "text" (default: true)
curly_single_quotes = true # 'text' to 'text' (default: true)
curly_apostrophes = false # it's to it's (default: false)
ellipsis = true # ... to ... (default: true)
en_dash = false # Disabled by default (use "--" to enable)
em_dash = "--" # -- to --- (default: "--", use false to disable)
Configuration values are validated at parse time. Invalid values will produce descriptive error messages:
$ hongdown --config invalid.toml input.md
Error: failed to parse configuration file: line_width must be at least 8, got 5.
When include patterns are configured, you can run Hongdown without
specifying files:
# Format all files matching include patterns
hongdown --write
# Check all files matching include patterns
hongdown --check
CLI options override configuration file settings:
# Use config file but override line width
hongdown --line-width 100 input.md
# Disable word wrapping (overrides config file)
hongdown --no-line-width input.md
# Use specific config file
hongdown --config /path/to/.hongdown.toml input.md
Hongdown enforces the following conventions:
= or -)###, ####, etc.)Document Title
==============
Section
-------
### Subsection
When sentence_case = true is set in the configuration, Hongdown automatically
converts headings to sentence case using intelligent heuristics:
# Development Commands → Development commands
# Working With JSON APIs → Working with JSON APIs
# Using `MyClass` In Code → Using `MyClass` in code
The converter:
API, HTTP)JSON-RPC)You can add custom proper nouns to preserve:
[heading]
sentence_case = true
proper_nouns = ["MyCompany", "MyProduct", "MyAPI"]
You can also exclude built-in proper nouns by treating them as common nouns. This is useful for words like “Go” which can be either a programming language or a common verb:
[heading]
sentence_case = true
common_nouns = ["Go", "Swift"] # Treat these as common nouns, not proper nouns
Built-in proper nouns include ~450 entries: programming languages (JavaScript, TypeScript, Python, Rust, Go), technologies (GitHub, Docker, React, Node.js), databases (PostgreSQL, MySQL, MongoDB), countries (United States, Republic of Korea), natural languages (English, Korean, Japanese), and more.
You can also use HTML comment directives to define document-specific proper nouns and common nouns. See the “HTML comment directives” section for details.
- (space-hyphen-two spaces)1. format - First item
- Second item
- Nested item
~~~~)~~~~ rust
fn main() {
println!("Hello, world!");
}
~~~~
You can configure external formatters for code blocks in your .hongdown.toml. This allows automatic formatting of embedded code using language-specific tools.
[code_block.formatters]
# Simple format: command as array
javascript = ["deno", "fmt", "--ext=js", "-"]
typescript = ["deno", "fmt", "--ext=ts", "-"]
rust = ["rustfmt"]
# With custom timeout (default is 5 seconds)
[code_block.formatters.python]
command = ["black", "-"]
timeout = 10
Behavior:
javascript matches javascript, not
js)To skip formatting for a specific code block, add hongdown-no-format after the
language identifier:
~~~~ python hongdown-no-format
def hello(): print("Hello, World!")
~~~~
The hongdown-no-format keyword is preserved in the output, ensuring the code
block remains unformatted on subsequent runs.
For WASM builds, use the formatWithCodeFormatter function with a callback:
import { formatWithCodeFormatter } from "@hongdown/wasm";
import * as prettier from "prettier";
const { output, warnings } = await formatWithCodeFormatter(markdown, {
codeFormatter: (language, code) => {
if (language === "javascript" || language === "typescript") {
return prettier.format(code, { parser: "babel" });
}
return null; // Keep original for other languages
},
});
$…$) and display ($$…$$) TeX/LaTeX math is preserved verbatim,
never escaped or punctuation-transformed (like code spans)$, $ command, or
$5 stays literal textmath = false in .hongdown.toml to treat every $ as textThe complexity is $O(n \log n)$.
$$
x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$
MDX documents mix Markdown with JavaScript and JSX. Because the underlying CommonMark parser does not understand those constructs, formatting an MDX document normally corrupts the embedded code. MDX mode (“first, do no harm”) detects the JS/JSX constructs and passes them through verbatim, while the surrounding Markdown prose is still formatted.
Within paragraph text, the constructs Hongdown would otherwise corrupt are preserved verbatim — never escaped or punctuation-transformed:
import/export statements, JSX elements and fragments (<Tabs …>,
<Chart … />, <>…</>), and {…} expressions (including {/* … */}
comments)--mdx or mdx = true in .hongdown.tomlimport { Chart } from "./chart.js";
export const meta = { author: "Hong Minhee" };
<PackageManagerTabs
command={{ npm: "npm add @scope/pkg" }}
/>
A paragraph of prose is still wrapped and punctuation-transformed, while the
JSX and imports above are left exactly as written.
A JSX tag that is already valid inline HTML (for example a single {value}
attribute) is left to the underlying parser, which keeps the tag while still
formatting the element's Markdown children; only tags that break the HTML
grammar (a {{…}} attribute, embedded quotes or spaces, or a multi-line opener)
are protected as a whole.
Some constructs are intentionally left unprotected: anything inside a heading
(an explicit {#id} anchor stays a heading anchor), braces inside inline code
(`{x}`), math ($\frac{1}{2}$), or link/image syntax, an expression
containing a regex literal whose context is ambiguous (a / immediately after
}), and an ESM object whose body contains a blank line.
See the [documentation] for more details.
[documentation]: https://example.com/docs
See STYLE.md for the complete style specification, including the philosophy behind these conventions and detailed formatting rules.
Install the Hongdown extension from the Visual Studio Marketplace or Open VSX Registry, then make it the default formatter for Markdown and MDX:
{
"[markdown]": {
"editor.defaultFormatter": "hongminhee.hongdown"
},
"[mdx]": {
"editor.defaultFormatter": "hongminhee.hongdown"
}
}
The extension uses a bundled WebAssembly build by default. To use a system Hongdown CLI instead:
{
"hongdown.backend": "cli",
"hongdown.cli.path": "hongdown"
}
By default, the extension looks for .hongdown.toml in the workspace root. For
a standalone file outside any workspace, it falls back to the file's containing
directory. Set hongdown.config.path to use a different configuration file.
Add the following to your Zed settings to use Hongdown as the Markdown formatter (contributed by Lee Dogeon):
{
"languages": {
"Markdown": {
"formatter": {
"external": {
"command": "hongdown",
"arguments": ["-"]
}
}
}
}
}
If you use none-ls.nvim (a community-maintained fork of null-ls.nvim), you can register Hongdown as a formatter (requires none-ls-extras.nvim, contributed by Vladimir Rubin):
local hongdown = require('none-ls.formatting.hongdown')
null_ls.register(hongdown)
If you use Helix, you can register Hongdown as a formatter (contributed by Jean Simard):
[[language]]
name = "markdown"
auto-format = true
formatter = { command = "hongdown", args = ["--stdin"] }
Hongdown can also be used as a Rust library:
use hongdown::{format, Options};
let input = "# Hello World\nThis is a paragraph.";
let options = Options::default();
let output = format(input, &options).unwrap();
println!("{}", output);
Hongdown is available as a WebAssembly-based library for JavaScript and TypeScript:
npm install @hongdown/wasm
import { format, formatWithWarnings, loadConfigFromToml } from "@hongdown/wasm";
// Basic usage
const markdown = "# Hello\nWorld";
const formatted = await format(markdown);
// With options
const result = await format(markdown, {
lineWidth: 100,
setextH1: false,
fenceChar: "`",
});
// Get warnings along with formatted output
const { output, warnings } = await formatWithWarnings(markdown);
if (warnings.length > 0) {
for (const warning of warnings) {
console.warn(`Line ${warning.line}: ${warning.message}`);
}
}
// Parse .hongdown.toml for WASM formatting
const { options: configOptions } = await loadConfigFromToml(configToml);
const configured = await format(markdown, configOptions);
External code block formatters configured in code_block.formatters are
reported as warnings because the WASM runtime cannot execute external
commands. Use the CLI for those settings.
The library works in Node.js, Bun, Deno, and web browsers. See the TypeScript type definitions for all available options.
This project uses mise for task management.
After cloning the repository, set up the Git pre-commit hook to automatically run quality checks before each commit:
mise generate git-pre-commit --task=check --write
The following tasks are available:
# Run all quality checks
mise run check
# Individual checks
mise run check:clippy # Run clippy linter
mise run check:fmt # Check code formatting
mise run check:type # Run Rust type checking
mise run check:markdown # Check Markdown formatting
See AGENTS.md for detailed development guidelines including TDD practices, code style conventions, and commit message guidelines.
The name Hongdown is a portmanteau of Hong (from Hong Minhee, the author) and Markdown. It also sounds like the Korean word hongdapda (홍답다), meaning “befitting of Hong” or “Hong-like.”
Distributed under the GPL-3.0-or-later. See LICENSE for more information.
Rust
89.6%
TypeScript
8.7%