Skip to content

Repository files navigation

asciidoc-mode

MELPA MELPA Stable CI License GPL 3

A tree-sitter-based major mode for editing AsciiDoc files in Emacs.

Features

  • Syntax highlighting via tree-sitter (headings, inline markup, blocks, lists, tables, admonitions, macros, attributes)

  • Native source-block highlighting — [source,lang] blocks are fontified with the language’s own major mode (toggle and size limit via asciidoc-fontify-code-blocks-natively)

  • Section navigation (C-M-a / C-M-e) and sentence-level movement (M-a / M-e) across paragraphs and blocks

  • Imenu support for section navigation

  • Outline minor mode enabled by default — heading navigation (C-c C-n/p/f/b/u) and folding

  • Structural editing — promote/demote headings (M-<left> / M-<right>) and move subtrees up/down (M-<up> / M-<down>)

  • Cross-reference and link navigation — follow [id]/xref: to their anchors (explicit ids, auto-generated section ids, or natural-title refs), including cross-file xref: targets (Antora page IDs and relative paths), and open URLs, via RET, mouse, C-c C-o, or xref (M-. / M-, with history); M-? lists references to the anchor at point; and completion-at-point offers cross-reference ids (in << / xref:), attribute names (in {), source-block languages (after [source,), and file paths (after include::)

  • On-the-fly validation — a Flymake backend runs the buffer through Asciidoctor and reports its warnings and errors inline (M-x flymake-mode)

  • Comment support (// line comments)

  • Graceful degradation — works as basic text-mode when grammars are unavailable

Rationale

I’ve been maintaining adoc-mode since 2022, but over time I realized I rarely used many of its advanced features (e.g. inline image preview, interactive markup commands, tempo templates). I wanted something simpler.

asciidoc-mode is my attempt to build a leaner alternative to adoc-mode, not a replacement for it. The key differences:

  • Tree-sitter for robust, accurate font-locking and optimal performance in large documents

  • Simple by design — focuses on the essentials (highlighting, navigation, folding) without the bells and whistles

  • Single-file implementation with minimal complexity

I named it asciidoc-mode instead of adoc-ts-mode because it shares nothing with adoc-mode and I wanted to emphasize that. There has never been an asciidoc-mode before, so asciidoc-ts-mode felt too verbose for no good reason.

adoc-mode will continue to be maintained and evolved separately. Pick whichever suits your workflow.

Feature asciidoc-mode adoc-mode

Syntax highlighting

Tree-sitter

Regex

Imenu

Yes

Yes

Outline / folding

Yes

Yes

Section navigation

Yes

No

Sentence navigation

Yes

No

Markup insertion commands

No

Yes

Tempo templates

No

Yes

Inline image preview

No

Yes

Title promotion / demotion

Yes

Yes

Native code block highlighting

Yes

Yes

Flyspell integration

Yes

Yes

Flycheck integration

Yes

Yes

Flymake integration

Yes

Yes

Compilation error support

No

Yes

Emacs requirement

30.1+

28.1+

Requirements

  • Emacs 30.1 or later (built with tree-sitter support)

  • Two tree-sitter grammars from cathaysia/tree-sitter-asciidoc:

    • asciidoc (block-level structure)

    • asciidoc_inline (inline formatting)

Installation

Grammar Setup

After installing the package, run:

M-x asciidoc-install-grammars

This downloads and compiles both grammars automatically. You only need to do this once.

Package Installation

MELPA

The package is available on MELPA and MELPA Stable. If you’ve configured MELPA as a package source:

M-x package-install RET asciidoc-mode RET

Or with use-package:

(use-package asciidoc-mode
  :ensure t)

use-package with vc

Alternatively, Emacs 30+ can install directly from VCS:

(use-package asciidoc-mode
  :vc (:url "https://github.com/bbatsov/asciidoc-mode" :rev :newest :branch "main"))

Manual

Clone the repository and add it to your load-path:

(add-to-list 'load-path "/path/to/asciidoc-mode")
(require 'asciidoc-mode)

Usage

Open any .adoc or .asciidoc file and asciidoc-mode activates automatically.

Antora

Antora is the most common way to build AsciiDoc documentation these days, so asciidoc-mode understands its project layout. A buffer is treated as an Antora page when it lives under modules/<module>/pages/ next to an antora.yml component descriptor.

In an Antora project, cross-reference navigation follows Antora’s page-id rules rather than plain file paths:

  • xref: targets resolve against the module’s pages/ directory, not the current file — so basics/installation.adoc opens modules/ROOT/pages/basics/installation.adoc and jumps to the matching section from any page. A module: prefix (other:page.adoc) is honored too.

  • Section ids are generated the way Antora does it (empty idprefix, - separator), so a #getting-started fragment finds the == Getting Started heading — not the _getting_started form Asciidoctor uses standalone.

Follow a reference the usual way (RET, mouse, C-c C-o, or M-.) and it opens the target file when needed.

Cross-component (component:) and versioned (version@) targets are not resolved — those live in separate repositories and are only knowable from a full Antora playbook, which is out of scope for an editor.

Linting

asciidoc-mode ships a Flymake backend that runs the current buffer through Asciidoctor and reports its parser warnings and errors inline. Turn it on with M-x flymake-mode (or from asciidoc-mode-hook). The check feeds the buffer to Asciidoctor over its standard input, so it works on unsaved edits; if asciidoctor isn’t on your PATH, point asciidoc-asciidoctor-command at it.

Prefer Flycheck? It already bundles built-in asciidoc and asciidoctor checkers, so M-x flycheck-mode gives you the same kind of validation without any extra setup from this package.

Design

asciidoc-mode uses two tree-sitter parsers from cathaysia/tree-sitter-asciidoc, each covering the full buffer independently:

  • asciidoc — block-level structure (headings, paragraphs, lists, code blocks, admonitions, attributes, comments, etc.)

  • asciidoc-inline — inline formatting (bold, italic, monospace, links, cross-references, footnotes, macros, replacements, etc.)

The grammar author split them because block-level and inline-level parsing have fundamentally different rules in AsciiDoc, and separating them keeps each grammar simpler. Both parsers operate on the entire buffer without range restrictions — this is the same dual-parser pattern used by Emacs’s built-in markdown-ts-mode.

Because both parsers run independently on the same text, they can disagree. In particular, the inline parser misinterprets and * list markers as emphasis delimiters, creating spurious emphasis spans that can swallow subsequent inline content (e.g. autolinks). To mitigate this, all block-level font-lock rules use :override t so that block-level faces (list markers, headings, code blocks, etc.) always win over incorrect inline faces. This doesn’t fix inline elements consumed by the spurious emphasis nodes — that requires an upstream grammar fix (see #2).

A related subtlety: treesit’s default font-lock behavior (:override nil) skips an entire captured range if any position within it already has a face. This means font-lock rules that capture a broad parent node will silently fail when a child node was already fontified by an earlier rule. To avoid this, inline macro rules capture specific child nodes (macro_name, target) rather than the whole inline_macro node — the same pattern used for block macros.

When grammars are unavailable, the mode falls back to plain text-mode with comment support only. No tree-sitter features are activated in this case.

Development

The project uses Eldev for build tooling and Buttercup for testing.

# Install Eldev (if not already installed)
curl -fsSL https://raw.github.com/emacs-eldev/eldev/master/webinstall/eldev | sh

# Run everything
make all

# Individual targets
make compile   # Byte-compile with warnings-as-errors
make lint      # Run elisp-lint
make test      # Run buttercup tests
make clean     # Remove build artifacts
Note
Font-lock tests require the tree-sitter grammars to be installed. Tests that need grammars will be skipped (not failed) if they are unavailable.

License

Licensed under the GNU General Public License v3.0 or later. See LICENSE for details.

About

A modern Emacs major mode for editing AsciiDoc files, powered by TreeSitter

Resources

Contributing

Stars

24 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages