branch: elpa/adoc-mode
commit 3c3665ad9069052d56934e94a6fa21958d0b667b
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Add Asciidoctor preview and export integration
Bridge the gap with AsciiDoc tooling elsewhere by shelling out to the
asciidoctor CLI. A new adoc-asciidoctor.el provides a preview that renders
the buffer (via stdin, so unsaved edits and relative paths work) into an
xwidget or eww side pane, a live-preview minor mode that refreshes on save,
and HTML/DocBook/PDF/EPUB export commands that run through compile. All of
it
hangs off a C-c C-c transient and the AsciiDoc menu, and degrades gracefully
when asciidoctor isn't installed.
Also broaden the compilation error matcher to recognise modern asciidoctor:
diagnostics, not just the legacy AsciiDoc.py asciidoc: format, so jumping to
warnings and errors actually works.
---
CHANGELOG.md | 5 +
README.adoc | 42 +++++-
adoc-asciidoctor.el | 263 +++++++++++++++++++++++++++++++++++++
adoc-mode.el | 16 ++-
test/adoc-mode-asciidoctor-test.el | 128 ++++++++++++++++++
5 files changed, 452 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 574f718bb1..ab6fd0766c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,13 @@
## main (unreleased)
+### New features
+
+- Add Asciidoctor integration for previewing and exporting documents,
reachable from the new `adoc-asciidoctor-menu` transient on `C-c C-c` (and the
AsciiDoc menu). `adoc-preview` renders the current buffer with `asciidoctor`
and shows the HTML in a side pane - an xwidget WebKit widget when available,
otherwise `eww`, configurable via `adoc-preview-backend` - and
`adoc-live-preview-mode` re-renders on every save. The preview feeds the buffer
to `asciidoctor` through its standard input, s [...]
+
### Changes
+- The compilation error matcher now also recognises modern `asciidoctor:`
diagnostics, not just the legacy AsciiDoc.py `asciidoc:` format, so jumping to
warnings and errors works with current Asciidoctor output.
- `[source,ocaml]` code blocks now fontify with `neocaml-mode` when it is
available, falling back to `tuareg-mode` and then `caml-mode`. To support this,
a value in `adoc-code-lang-modes` may now be either a single major mode or a
list of candidate modes tried in order (the first defined one wins).
### Bugs fixed
diff --git a/README.adoc b/README.adoc
index b15d9886ea..883fdac15c 100644
--- a/README.adoc
+++ b/README.adoc
@@ -51,7 +51,8 @@ Here are some of the main features of `adoc-mode`:
- navigate to anchors (`C-c C-a`) and follow URLs, `include::` macros, and
xrefs at point (`C-c C-o` / `M-.`)
- nested `imenu` index with hierarchical heading structure
- outline folding built on `outline-minor-mode` (enabled out of the box):
`TAB` cycles the subtree at point, `S-TAB` cycles the whole buffer (overview /
contents / show all), one-line title style only
-- integration with `flyspell-mode` (skips non-prose regions), comment commands
(`M-;`), and compilation mode
+- preview and export via Asciidoctor (`C-c C-c`): live HTML preview in an
xwidget or `eww` side pane, plus export to HTML, DocBook, PDF, and EPUB with
navigable warnings and errors
+- integration with `flyspell-mode` (skips non-prose regions) and comment
commands (`M-;`)
- filling that respects AsciiDoc hard line breaks (a line ending in ` +`) and
section titles
=== Demo
@@ -109,6 +110,45 @@ from the _AsciiDoc_ menu in the menu bar.
This section only describes some not so obvious features.
+=== Preview and Export
+
+`adoc-mode` can render and export documents by shelling out to the
+https://asciidoctor.org/[Asciidoctor] command-line tool, so you need
+`asciidoctor` on your `PATH` for these commands to work. PDF and EPUB export
+additionally need the `asciidoctor-pdf` and `asciidoctor-epub3` converters.
+
+Press kbd:[C-c C-c] to open the _Asciidoctor_ transient menu, or use the
+_AsciiDoc → Preview / Export_ menu. From there you can:
+
+- `adoc-preview` - render the current buffer and show the HTML in a side pane.
+- `adoc-live-preview-mode` - a minor mode that re-renders the preview every
time
+ you save the buffer.
+- `adoc-export-html` / `adoc-export-docbook` / `adoc-export-pdf` /
+ `adoc-export-epub` - export the file next to the source. These run through
+ `compile`, so you can jump to any warnings or errors with kbd:[M-g M-n].
+
+The preview feeds the buffer to Asciidoctor through its standard input, so it
+reflects unsaved edits and resolves relative `include::` and image paths.
+
+The preview pane is chosen by `adoc-preview-backend`. By default
+(`auto`) it uses an embedded WebKit widget when your Emacs was built with
+xwidget support, and falls back to `eww` otherwise. You can force a specific
+backend:
+
+[source,emacs-lisp]
+----
+;; one of: auto (default), xwidget, eww, browser
+(setq adoc-preview-backend 'eww)
+----
+
+Pass extra arguments to every Asciidoctor invocation via
+`adoc-asciidoctor-extra-args`, e.g. to enable section numbers:
+
+[source,emacs-lisp]
+----
+(setq adoc-asciidoctor-extra-args '("-a" "sectnums"))
+----
+
=== Image Preview
If you click kbd:[mouse-3] on an image link like
diff --git a/adoc-asciidoctor.el b/adoc-asciidoctor.el
new file mode 100644
index 0000000000..b8704ff126
--- /dev/null
+++ b/adoc-asciidoctor.el
@@ -0,0 +1,263 @@
+;;; adoc-asciidoctor.el --- Asciidoctor integration for adoc-mode -*-
lexical-binding: t; -*-
+;;
+;; Copyright 2022-2026 Bozhidar Batsov <[email protected]> and adoc-mode
contributors
+;;
+;; This file is not part of GNU Emacs.
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+;; Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; Preview and export support for `adoc-mode' that shells out to the
+;; `asciidoctor' command-line tool. AsciiDoc, unlike Markdown, needs the real
+;; processor to render faithfully (attributes, `include::', conditionals,
+;; tables), so we delegate to Asciidoctor rather than approximating in Elisp.
+;;
+;; The entry point is the `adoc-asciidoctor-menu' transient (bound to
+;; `C-c C-c' in `adoc-mode'). From there you can render a preview in an
+;; xwidget (falling back to `eww') or export to HTML, DocBook, PDF and EPUB.
+;; Export runs through `compile' so Asciidoctor's warnings and errors are
+;; navigable.
+
+;;; Code:
+
+(require 'compile)
+(require 'subr-x)
+(require 'transient)
+
+(declare-function xwidget-webkit-browse-url "xwidget" (url &optional
new-session))
+(declare-function eww-open-file "eww" (file &optional new-buffer))
+
+;;; Customization
+
+(defgroup adoc-asciidoctor nil
+ "Asciidoctor integration for `adoc-mode'."
+ :group 'adoc
+ :prefix "adoc-")
+
+(defcustom adoc-asciidoctor-command "asciidoctor"
+ "Name of, or path to, the Asciidoctor executable."
+ :type 'string
+ :group 'adoc-asciidoctor)
+
+(defcustom adoc-asciidoctor-extra-args nil
+ "Extra command-line arguments passed to every Asciidoctor invocation.
+Each element is a separate argument string, for example
+\"-a\" followed by \"sectnums\"."
+ :type '(repeat string)
+ :group 'adoc-asciidoctor)
+
+(defcustom adoc-preview-backend 'auto
+ "How `adoc-preview' displays the rendered HTML.
+The value is one of the following symbols:
+
+`auto' Use an xwidget when one is available, otherwise `eww'.
+`xwidget' Render in an embedded WebKit widget (needs xwidget support).
+`eww' Render in Emacs' built-in `eww' browser.
+`browser' Open in the external browser via `browse-url'."
+ :type '(choice (const :tag "Auto-detect" auto)
+ (const :tag "Embedded WebKit (xwidget)" xwidget)
+ (const :tag "Emacs Web Wowser (eww)" eww)
+ (const :tag "External browser" browser))
+ :group 'adoc-asciidoctor)
+
+;;; Running Asciidoctor
+
+(defun adoc--asciidoctor-ensure ()
+ "Signal a `user-error' unless the Asciidoctor executable is available."
+ (unless (executable-find adoc-asciidoctor-command)
+ (user-error "Cannot find the Asciidoctor executable %S; \
+customize `adoc-asciidoctor-command'" adoc-asciidoctor-command)))
+
+(defun adoc--asciidoctor-source-file ()
+ "Return the file the current buffer visits, or signal a `user-error'."
+ (or buffer-file-name
+ (user-error "Current buffer is not visiting a file")))
+
+(defvar-local adoc--preview-file nil
+ "Absolute path to the current buffer's temporary preview HTML file.")
+
+(defun adoc--asciidoctor-render-preview ()
+ "Render the current buffer to its preview HTML file.
+Return the file on success, or nil on failure (so callers can avoid
+displaying a blank or stale page). The buffer contents are piped
+through Asciidoctor's standard input, so unsaved edits are included and
+no save is required. The base directory and the output file both live
+in `default-directory', which keeps relative `include::' and image paths
+resolving correctly when the generated HTML is loaded over `file://'.
+
+Asciidoctor exits 0 even on a recoverable ERROR when reading from
+standard input, so any diagnostics on its error output are reported
+regardless of the exit status."
+ (adoc--asciidoctor-ensure)
+ (let ((base (expand-file-name default-directory))
+ (errfile (make-temp-file "adoc-asciidoctor-err")))
+ (unless (and adoc--preview-file (file-exists-p adoc--preview-file))
+ (setq adoc--preview-file
+ (make-temp-file (expand-file-name "adoc-preview-" base) nil
".html")))
+ (unwind-protect
+ (let ((status (apply #'call-process-region
+ (point-min) (point-max)
+ adoc-asciidoctor-command nil
+ (list nil errfile) nil
+ (append adoc-asciidoctor-extra-args
+ (list "-B" base
+ "-b" "html5"
+ "-o" adoc--preview-file
+ "-"))))
+ (errors (with-temp-buffer
+ (insert-file-contents errfile)
+ (string-trim (buffer-string)))))
+ (unless (string-empty-p errors)
+ (message "Asciidoctor: %s" errors))
+ ;; Only hand back the file when the run actually succeeded; a
+ ;; non-zero exit may leave the output empty or untouched.
+ (and (integerp status) (zerop status) adoc--preview-file))
+ (delete-file errfile))))
+
+(defun adoc--asciidoctor-compile (backend-args)
+ "Run Asciidoctor on the current file with BACKEND-ARGS through `compile'.
+The buffer is saved first when modified so the on-disk file is current.
+Asciidoctor is invoked with the file's base name from its own directory,
+so the locations it reports resolve against `default-directory' in the
+compilation buffer."
+ (adoc--asciidoctor-ensure)
+ (let ((file (adoc--asciidoctor-source-file)))
+ (when (buffer-modified-p)
+ (save-buffer))
+ (let* ((default-directory (file-name-directory file))
+ (command (mapconcat
+ #'shell-quote-argument
+ (append (list adoc-asciidoctor-command)
+ adoc-asciidoctor-extra-args
+ backend-args
+ (list (file-name-nondirectory file)))
+ " ")))
+ (compilation-start
+ command nil
+ (lambda (_mode)
+ (format "*asciidoctor: %s*" (file-name-nondirectory file)))))))
+
+;;; Export commands
+
+;;;###autoload
+(defun adoc-export-html ()
+ "Export the current AsciiDoc buffer to HTML5."
+ (interactive)
+ (adoc--asciidoctor-compile '("-b" "html5")))
+
+;;;###autoload
+(defun adoc-export-docbook ()
+ "Export the current AsciiDoc buffer to DocBook 5."
+ (interactive)
+ (adoc--asciidoctor-compile '("-b" "docbook5")))
+
+;;;###autoload
+(defun adoc-export-pdf ()
+ "Export the current AsciiDoc buffer to PDF.
+Requires the `asciidoctor-pdf' converter to be installed."
+ (interactive)
+ (adoc--asciidoctor-compile '("-r" "asciidoctor-pdf" "-b" "pdf")))
+
+;;;###autoload
+(defun adoc-export-epub ()
+ "Export the current AsciiDoc buffer to EPUB3.
+Requires the `asciidoctor-epub3' converter to be installed."
+ (interactive)
+ (adoc--asciidoctor-compile '("-r" "asciidoctor-epub3" "-b" "epub3")))
+
+;;; Preview
+
+(defun adoc--preview-resolve-backend ()
+ "Resolve `adoc-preview-backend' to a concrete backend symbol."
+ (if (eq adoc-preview-backend 'auto)
+ (if (and (display-graphic-p)
+ (fboundp 'xwidget-webkit-browse-url))
+ 'xwidget
+ 'eww)
+ adoc-preview-backend))
+
+(defun adoc--preview-display (file)
+ "Display preview FILE according to the resolved backend.
+Focus stays in the source buffer so the viewer acts as a side pane."
+ (let ((url (concat "file://" file))
+ (backend (adoc--preview-resolve-backend)))
+ (pcase backend
+ ('browser (browse-url url))
+ ('xwidget
+ (save-selected-window
+ (xwidget-webkit-browse-url url))
+ (when-let* ((buf (get-buffer "*xwidget-webkit*")))
+ (display-buffer-in-side-window
+ buf '((side . right) (window-width . 0.5)))))
+ ('eww
+ (require 'eww)
+ (save-window-excursion
+ (eww-open-file file))
+ (when-let* ((buf (get-buffer "*eww*")))
+ (display-buffer-in-side-window
+ buf '((side . right) (window-width . 0.5)))))
+ (_ (error "Unknown `adoc-preview-backend': %S" backend)))))
+
+(defun adoc--preview-update ()
+ "Re-render the current buffer and (re)display its preview.
+Does nothing visible when the render fails, leaving any previous
+preview in place rather than showing a blank page."
+ (when-let* ((file (adoc--asciidoctor-render-preview)))
+ (adoc--preview-display file)))
+
+(defun adoc--preview-cleanup ()
+ "Delete the current buffer's temporary preview file, if any."
+ (when (and adoc--preview-file (file-exists-p adoc--preview-file))
+ (ignore-errors (delete-file adoc--preview-file)))
+ (setq adoc--preview-file nil))
+
+;;;###autoload
+(defun adoc-preview ()
+ "Render the current AsciiDoc buffer and show it in a preview pane."
+ (interactive)
+ (adoc--preview-update))
+
+;;;###autoload
+(define-minor-mode adoc-live-preview-mode
+ "Re-render the AsciiDoc preview whenever the buffer is saved."
+ :lighter " AdocPreview"
+ (if adoc-live-preview-mode
+ (progn
+ (add-hook 'after-save-hook #'adoc--preview-update nil t)
+ (add-hook 'kill-buffer-hook #'adoc--preview-cleanup nil t)
+ (adoc--preview-update))
+ (remove-hook 'after-save-hook #'adoc--preview-update t)
+ (remove-hook 'kill-buffer-hook #'adoc--preview-cleanup t)
+ (adoc--preview-cleanup)))
+
+;;; Transient menu
+
+;;;###autoload (autoload 'adoc-asciidoctor-menu "adoc-asciidoctor" nil t)
+(transient-define-prefix adoc-asciidoctor-menu ()
+ "Preview and export the current AsciiDoc document with Asciidoctor."
+ ["Preview"
+ ("p" "Preview" adoc-preview)
+ ("l" "Live preview mode" adoc-live-preview-mode)]
+ ["Export"
+ ("h" "HTML5" adoc-export-html)
+ ("d" "DocBook 5" adoc-export-docbook)
+ ("f" "PDF" adoc-export-pdf)
+ ("e" "EPUB3" adoc-export-epub)])
+
+(provide 'adoc-asciidoctor)
+
+;;; adoc-asciidoctor.el ends here
diff --git a/adoc-mode.el b/adoc-mode.el
index 3a48c92577..6e49a25ea2 100644
--- a/adoc-mode.el
+++ b/adoc-mode.el
@@ -53,6 +53,7 @@
(require 'subr-x)
(require 'adoc-mode-image)
(require 'adoc-mode-tempo)
+(require 'adoc-asciidoctor)
(defconst adoc-mode-version "0.9.0"
"adoc mode version number.")
@@ -4061,6 +4062,7 @@ ITEMS is a list of (name pos . level)."
(define-key map "\C-c\C-a" 'adoc-goto-ref-label)
(define-key map "\C-c\C-o" 'adoc-follow-thing-at-point)
(define-key map (kbd "M-.") 'adoc-follow-thing-at-point)
+ (define-key map "\C-c\C-c" 'adoc-asciidoctor-menu)
(easy-menu-define adoc-mode-menu map "Menu for adoc mode"
`("AsciiDoc"
["Next heading" adoc-next-visible-heading]
@@ -4092,6 +4094,16 @@ ITEMS is a list of (name pos . level)."
["Follow thing at point" adoc-follow-thing-at-point]
["Goto anchor" adoc-goto-ref-label]
"---"
+ ("Preview / Export"
+ ["Preview" adoc-preview]
+ ["Live preview mode" adoc-live-preview-mode
+ :style toggle :selected adoc-live-preview-mode]
+ "---"
+ ["Export to HTML5" adoc-export-html]
+ ["Export to DocBook 5" adoc-export-docbook]
+ ["Export to PDF" adoc-export-pdf]
+ ["Export to EPUB3" adoc-export-epub])
+ "---"
;; names|wording / rough order/ help texts are from asciidoc manual
("Templates / cheat sheet"
("Text formatting - constrained quotes"
@@ -4313,9 +4325,11 @@ Turning on Adoc mode runs the normal hook
`adoc-mode-hook'."
(setq-local imenu-create-index-function adoc-imenu-create-index-function)
;; compilation
+ ;; Matches both the modern Asciidoctor (`asciidoctor: ...') and the legacy
+ ;; Python AsciiDoc (`asciidoc: ...') diagnostic formats.
(add-to-list 'compilation-error-regexp-alist-alist
'(asciidoc
- "^asciidoc: +\\(?:ERROR\\|\\(WARNING\\|DEPRECATED\\)\\):
+\\([^:\n]*\\): line +\\([0-9]+\\)"
+ "^asciidoc\\(?:tor\\)?:
+\\(?:ERROR\\|\\(WARNING\\|DEPRECATED\\)\\): +\\([^:\n]*\\): line +\\([0-9]+\\)"
2 3 nil (1 . nil)))
(setq-local compilation-error-regexp-alist
(cons 'asciidoc compilation-error-regexp-alist))
diff --git a/test/adoc-mode-asciidoctor-test.el
b/test/adoc-mode-asciidoctor-test.el
new file mode 100644
index 0000000000..01dcece6f7
--- /dev/null
+++ b/test/adoc-mode-asciidoctor-test.el
@@ -0,0 +1,128 @@
+;;; adoc-mode-asciidoctor-test.el --- Asciidoctor integration tests -*-
lexical-binding: t; -*-
+
+;; Copyright © 2026 Bozhidar Batsov
+
+;;; Commentary:
+
+;; Buttercup tests for the Asciidoctor integration layer: the executable
+;; guard, export command-line construction, preview backend resolution, and
+;; the compilation error regexp that makes Asciidoctor diagnostics navigable.
+;; These tests mock out the actual shelling out, so they need no `asciidoctor'
+;; on PATH.
+
+;;; Code:
+
+(require 'adoc-mode-test-helpers)
+(require 'adoc-asciidoctor)
+(require 'cl-lib)
+
+(describe "adoc--asciidoctor-ensure"
+ (it "throws a user-error when the executable is missing"
+ (spy-on 'executable-find :and-return-value nil)
+ (expect (adoc--asciidoctor-ensure) :to-throw 'user-error))
+
+ (it "returns normally when the executable is found"
+ (spy-on 'executable-find :and-return-value "/usr/bin/asciidoctor")
+ (expect (adoc--asciidoctor-ensure) :not :to-throw)))
+
+(describe "adoc--asciidoctor-source-file"
+ (it "throws a user-error for a non-file buffer"
+ (with-temp-buffer
+ (expect (adoc--asciidoctor-source-file) :to-throw 'user-error))))
+
+(describe "adoc-export commands"
+ (before-each
+ (spy-on 'executable-find :and-return-value "/usr/bin/asciidoctor")
+ (spy-on 'compilation-start))
+
+ (cl-flet ((command-for (export-fn)
+ ;; Visit a real temp file so `adoc--asciidoctor-compile' has a
+ ;; file to operate on, run the export, and return the command
+ ;; string handed to `compilation-start'.
+ (let ((file (make-temp-file "adoc-export-" nil ".adoc")))
+ (unwind-protect
+ (with-current-buffer (find-file-noselect file)
+ (funcall export-fn)
+ (spy-calls-most-recent 'compilation-start))
+ (when (get-file-buffer file)
+ (kill-buffer (get-file-buffer file)))
+ (delete-file file)))))
+
+ (it "builds an HTML5 command"
+ (let ((args (spy-context-args (command-for #'adoc-export-html))))
+ (expect (car args) :to-match "asciidoctor")
+ (expect (car args) :to-match "-b html5")))
+
+ (it "builds a DocBook command"
+ (let ((args (spy-context-args (command-for #'adoc-export-docbook))))
+ (expect (car args) :to-match "-b docbook5")))
+
+ (it "builds a PDF command requiring asciidoctor-pdf"
+ (let ((args (spy-context-args (command-for #'adoc-export-pdf))))
+ (expect (car args) :to-match "-r asciidoctor-pdf")
+ (expect (car args) :to-match "-b pdf")))
+
+ (it "builds an EPUB3 command requiring asciidoctor-epub3"
+ (let ((args (spy-context-args (command-for #'adoc-export-epub))))
+ (expect (car args) :to-match "-r asciidoctor-epub3")
+ (expect (car args) :to-match "-b epub3")))))
+
+(describe "adoc--preview-resolve-backend"
+ (it "passes explicit backends through unchanged"
+ (dolist (backend '(xwidget eww browser))
+ (let ((adoc-preview-backend backend))
+ (expect (adoc--preview-resolve-backend) :to-be backend))))
+
+ (it "auto resolves to eww on a terminal"
+ (let ((adoc-preview-backend 'auto))
+ (spy-on 'display-graphic-p :and-return-value nil)
+ (expect (adoc--preview-resolve-backend) :to-be 'eww)))
+
+ (it "auto resolves to xwidget when graphical with xwidget support"
+ (let ((adoc-preview-backend 'auto))
+ (spy-on 'display-graphic-p :and-return-value t)
+ (cl-letf (((symbol-function 'xwidget-webkit-browse-url) #'ignore))
+ (expect (adoc--preview-resolve-backend) :to-be 'xwidget)))))
+
+(describe "adoc--preview-update"
+ (it "displays the rendered file on success"
+ (spy-on 'adoc--asciidoctor-render-preview :and-return-value "/tmp/x.html")
+ (spy-on 'adoc--preview-display)
+ (adoc--preview-update)
+ (expect 'adoc--preview-display :to-have-been-called-with "/tmp/x.html"))
+
+ (it "does not display anything when the render fails"
+ (spy-on 'adoc--asciidoctor-render-preview :and-return-value nil)
+ (spy-on 'adoc--preview-display)
+ (adoc--preview-update)
+ (expect 'adoc--preview-display :not :to-have-been-called)))
+
+(describe "the asciidoc compilation regexp"
+ ;; The entry is registered in `compilation-error-regexp-alist-alist' by
+ ;; `adoc-mode', so spin up an adoc buffer to populate it.
+ (let (re)
+ (before-all
+ (with-adoc-buffer "= Title\n"
+ (setq re (car (alist-get 'asciidoc
compilation-error-regexp-alist-alist)))))
+
+ (it "matches modern Asciidoctor errors"
+ (let ((line "asciidoctor: ERROR: doc.adoc: line 5: include file not
found"))
+ (expect (string-match re line) :to-be 0)
+ (expect (match-string 2 line) :to-equal "doc.adoc")
+ (expect (match-string 3 line) :to-equal "5")))
+
+ (it "matches modern Asciidoctor warnings"
+ (let ((line "asciidoctor: WARNING: doc.adoc: line 1: unterminated table
block"))
+ (expect (string-match re line) :to-be 0)
+ (expect (match-string 2 line) :to-equal "doc.adoc")
+ (expect (match-string 3 line) :to-equal "1")))
+
+ (it "still matches the legacy Python asciidoc format"
+ (let ((line "asciidoc: WARNING: doc.txt: line 9: missing"))
+ (expect (string-match re line) :to-be 0)
+ (expect (match-string 2 line) :to-equal "doc.txt")
+ (expect (match-string 3 line) :to-equal "9")))))
+
+(provide 'adoc-mode-asciidoctor-test)
+
+;;; adoc-mode-asciidoctor-test.el ends here