branch: elpa/adoc-mode
commit fc2e5376005aaab26d7a1406802fc4ea5a6dda93
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Treat +text+ / ++text++ as passthroughs, not monospace
In modern AsciiDoc the backtick is the only monospace delimiter; the
single and double plus are inline passthroughs (constrained and
unconstrained) that render as normal text with inline formatting
suppressed. They were left over from AsciiDoc.py compat-mode and
highlighted with the monospace face.
Add adoc-kw-inline-passthrough and register +/++ in the passthrough
section ahead of the quote keywords, so the enclosed text is reserved
(formatting suppressed) and shown in the default face, with the
delimiters de-emphasised. Drop the old monospace quote registrations.
---
CHANGELOG.md | 1 +
adoc-mode.el | 34 ++++++++++++++++++++++++++-----
doc/spec-compliance.adoc | 52 +++++++++++++++++++++++++++++-------------------
test/adoc-mode-test.el | 35 ++++++++++++++++++++++++++++----
4 files changed, 93 insertions(+), 29 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0231779258..f33e21a365 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@
### Bugs fixed
+- `+text+` and `++text++` are no longer highlighted as monospace. In modern
AsciiDoc the backtick is the only monospace delimiter; the single and double
plus are *inline passthroughs* (constrained and unconstrained), rendered as
normal text with inline formatting suppressed. They are now fontified as
passthroughs - the delimiters are de-emphasised and the enclosed text keeps the
default face with formatting suppressed - rather than reusing the monospace
face left over from the old AsciiD [...]
- Recognize level-5 section titles (`====== Title`). Previously
`adoc-title-max-level` was off by one, so the deepest heading level supported
by AsciiDoc was treated as ordinary text. Title promotion/demotion now cycles
through all six one-line levels and the five two-line levels independently.
## 0.8.0 (2026-02-21)
diff --git a/adoc-mode.el b/adoc-mode.el
index a807a39108..de8d21dc17 100644
--- a/adoc-mode.el
+++ b/adoc-mode.el
@@ -1955,6 +1955,24 @@ face-spec expression (e.g. from
`adoc-facespec-subscript')."
'(3 nil)) ; grumbl, I don't know how to get rid of it
`(4 '(face ,(or del-face 'adoc-meta-hide-face) adoc-reserved t) t))));
close del
+(defun adoc-kw-inline-passthrough (type ldel)
+ "Return a keyword for an inline passthrough delimited by LDEL.
+TYPE is `adoc-constrained' for a single-plus passthrough (`+x+')
+or `adoc-unconstrained' for a double-plus one (`++x++').
+
+Unlike the backtick, the plus delimiters are not monospace
+formatting in modern AsciiDoc: a passthrough is rendered as
+normal output text with inline formatting suppressed. So the
+enclosed text is left in the default face but marked reserved, so
+the quote keywords do not format it, and the delimiters are
+de-emphasised."
+ (list
+ `(lambda (end) (adoc-kwf-std end ,(adoc-re-quote type ldel) '(1 2 4) '(3)))
+ '(1 '(face adoc-meta-face adoc-reserved t) t t) ; attribute list
+ '(2 '(face adoc-meta-hide-face adoc-reserved t) t) ; opening delimiter
+ '(3 '(face nil adoc-reserved t) t) ; passthrough text
+ '(4 '(face adoc-meta-hide-face adoc-reserved t) t))) ; closing delimiter
+
(defconst adoc-re-escaped-formatting
"\\(\\\\\\)\\(\\([*_`#+^~]\\)\\3*\\)"
"Regexp matching a backslash-escaped inline formatting delimiter.
@@ -2608,6 +2626,14 @@ for multiline constructs to be matched."
(adoc-kw-quote 'adoc-unconstrained "+++" 'adoc-typewriter-face nil nil t)
;; $$text$$ passthrough (special characters escaped)
(adoc-kw-quote 'adoc-unconstrained "$$" 'adoc-typewriter-face nil nil t)
+ ;; +text+ / ++text++ inline passthroughs. In modern AsciiDoc these are
+ ;; passthroughs (formatting suppressed, rendered as normal text), NOT
+ ;; monospace; only the backtick is monospace. Must precede the quote
+ ;; keywords so the enclosed text is reserved before they scan it. The
+ ;; double (unconstrained) form is registered first so it wins over the
+ ;; single (constrained) one.
+ (adoc-kw-inline-passthrough 'adoc-unconstrained "++")
+ (adoc-kw-inline-passthrough 'adoc-constrained "+")
;; special characters
;; ------------------
@@ -2620,11 +2646,9 @@ for multiline constructs to be matched."
;; ------------------------------
(adoc-kw-quote 'adoc-unconstrained "**" 'adoc-bold-face)
(adoc-kw-quote 'adoc-constrained "*" 'adoc-bold-face)
- ;; The old AsciiDoc ``text'' (double quote) and `text' (single quote)
- ;; syntaxes are deprecated in Asciidoctor. Backtick inline code is
- ;; handled in the passthrough section above.
- (adoc-kw-quote 'adoc-unconstrained "++" 'adoc-typewriter-face)
- (adoc-kw-quote 'adoc-constrained "+" 'adoc-typewriter-face)
+ ;; The +text+ / ++text++ monospace markup of legacy AsciiDoc.py
+ ;; "compat-mode" is gone; the plus forms are inline passthroughs, handled
+ ;; in the passthrough section above. Backtick is the only monospace mark.
(adoc-kw-quote 'adoc-unconstrained "__" 'adoc-emphasis-face)
(adoc-kw-quote 'adoc-constrained "_" 'adoc-emphasis-face)
(adoc-kw-quote 'adoc-unconstrained "##" 'adoc-highlight-face) ; highlighted
text
diff --git a/doc/spec-compliance.adoc b/doc/spec-compliance.adoc
index 90ef2995ba..55031e1735 100644
--- a/doc/spec-compliance.adoc
+++ b/doc/spec-compliance.adoc
@@ -37,9 +37,10 @@ Findings are graded:
| Area | Finding | Severity
| text
-| `+text+` / `++text++` are still highlighted as monospace (legacy
- AsciiDoc.py "compat-mode"). In modern AsciiDoc the backtick is the only
- monospace delimiter; `+` is a passthrough delimiter, not monospace.
+| [.line-through]#`+text+` / `++text++` are still highlighted as monospace
+ (legacy AsciiDoc.py "compat-mode").# *FIXED* - they are now treated as
+ inline passthroughs (normal text, formatting suppressed); the backtick is
+ the only monospace delimiter.
| High
| text
@@ -93,11 +94,13 @@ Findings are graded:
| Supported
| -
-| Monospace `+x+` / `++x++`
-| *Not* monospace in modern AsciiDoc (legacy compat-mode only); `+` is a
- passthrough delimiter
-| Still highlighted as monospace (`adoc-typewriter-face`)
-| High
+| Passthrough `+x+` / `++x++`
+| Inline passthroughs (constrained/unconstrained), rendered as normal text
+ with formatting suppressed; not monospace
+| *Fixed* - treated as passthroughs (`adoc-kw-inline-passthrough`):
+ delimiters de-emphasised, content left in the default face and reserved
+ so inner formatting is not applied
+| -
| Highlight `#x#` / `##x##`
| Marked text
@@ -359,19 +362,28 @@ These are largely conversion concerns with little or no
editor surface:
== Recommended follow-ups (prioritised)
-. *Stop highlighting `+text+` / `++text++` as monospace by default.* This
- is the clearest spec drift. Consider gating the legacy behaviour behind
- a `compat-mode` defcustom (default off) so users who set `:compat-mode:`
- can re-enable it.
-. *Recognise checklist items* (`* [ ]`, `* [x]`, `* [*]`) with a dedicated
- face for the checkbox.
-. *Recognise the block-attribute ID/role/option shorthand* `[#id]`,
+[NOTE]
+====
+Items are checked off as they are addressed. The legacy `compat-mode`
+monospace behaviour was simply dropped rather than gated behind a
+defcustom, since it is no longer part of the language Asciidoctor
+implements.
+====
+
+. [x] *Stop highlighting `+text+` / `++text++` as monospace.* Done -
+ reclassified as inline passthroughs (`adoc-kw-inline-passthrough`).
+ Note: the menu / tempo templates still offer `+...+` / `++...++` under a
+ "Monospaced" label; fold that into the template cleanup (item 5).
+. [ ] *Recognise checklist items* (`* [ ]`, `* [x]`, `* [*]`) with a
+ dedicated face for the checkbox.
+. [ ] *Recognise the block-attribute ID/role/option shorthand* `[#id]`,
`[#id.role]`, `[%opt]` on block attribute lines, and treat `[#id]` as a
block anchor for `adoc-goto-ref-label` / xref following.
-. *Add modern curved-quote highlighting* `+"`text`"+` and ``'`text`'``.
-. *Drop or de-emphasise the deprecated `` `text' `` / ``\`\`text''``
- templates* in the menu, or relabel them as legacy.
-. *Add the `icon:name[]` inline macro* to the recognised macro set
+. [ ] *Add modern curved-quote highlighting* `+"`text`"+` and ``'`text`'``.
+. [ ] *Drop or de-emphasise the deprecated `` `text' `` / ``\`\`text''``
+ templates* in the menu (and the `+...+` / `++...++` "Monospaced"
+ templates), or relabel them as legacy.
+. [ ] *Add the `icon:name[]` inline macro* to the recognised macro set
(currently unhandled).
-. *Verify* CSV/DSV table formats, counters, and the literal-monospace
+. [ ] *Verify* CSV/DSV table formats, counters, and the literal-monospace
`` `+text+` `` enclosure, and add handling where it is missing.
diff --git a/test/adoc-mode-test.el b/test/adoc-mode-test.el
index a21877682f..2234295e69 100644
--- a/test/adoc-mode-test.el
+++ b/test/adoc-mode-test.el
@@ -566,8 +566,9 @@ Don't use it for anything real.")
"Lorem " nil "$$" 'adoc-meta-hide-face " ipsum "
'(adoc-typewriter-face adoc-verbatim-face) "$$" 'adoc-meta-hide-face " dolor\n"
nil
"Lorem " nil "**" 'adoc-meta-hide-face " ipsum "
'adoc-bold-face "**" 'adoc-meta-hide-face " dolor\n" nil
"Lorem " nil "*" 'adoc-meta-hide-face "ipsum"
'adoc-bold-face "*" 'adoc-meta-hide-face " dolor\n" nil
- "Lorem " nil "++" 'adoc-meta-hide-face " ipsum "
'adoc-typewriter-face "++" 'adoc-meta-hide-face " dolor\n" nil
- "Lorem " nil "+" 'adoc-meta-hide-face "ipsum"
'adoc-typewriter-face "+" 'adoc-meta-hide-face " dolor\n" nil
+ ;; +text+ / ++text++ are passthroughs now (normal text, not
monospace)
+ "Lorem " nil "++" 'adoc-meta-hide-face " ipsum " 'no-face
"++" 'adoc-meta-hide-face " dolor\n" nil
+ "Lorem " nil "+" 'adoc-meta-hide-face "ipsum" 'no-face "+"
'adoc-meta-hide-face " dolor\n" nil
"Lorem " nil "__" 'adoc-meta-hide-face " ipsum "
'adoc-emphasis-face "__" 'adoc-meta-hide-face " dolor\n" nil
"Lorem " nil "_" 'adoc-meta-hide-face "ipsum"
'adoc-emphasis-face "_" 'adoc-meta-hide-face " dolor\n" nil
"Lorem " nil "##" 'adoc-meta-hide-face " ipsum "
'adoc-highlight-face "##" 'adoc-meta-hide-face " dolor\n" nil
@@ -621,7 +622,7 @@ Don't use it for anything real.")
"~~~~~~~~~~~" 'adoc-meta-hide-face "\n" nil
"." 'adoc-meta-face "lorem " 'adoc-gen-face "_"
'adoc-meta-hide-face "ipsum" '(adoc-gen-face adoc-emphasis-face) "_"
'adoc-meta-hide-face "\n" nil
"\n" nil
- "lorem " 'adoc-gen-face "+" 'adoc-meta-hide-face "ipsum"
'(adoc-gen-face adoc-typewriter-face) "+" 'adoc-meta-hide-face " sit"
'adoc-gen-face "::" 'adoc-list-face " " 'adoc-align-face
+ "lorem " 'adoc-gen-face "+" 'adoc-meta-hide-face "ipsum"
'no-face "+" 'adoc-meta-hide-face " sit" 'adoc-gen-face "::" 'adoc-list-face "
" 'adoc-align-face
)))
;; test border cases where the quote delimiter is at the beginning and/or the
@@ -826,7 +827,7 @@ on top of the `<mark>' default)."
"------------" 'adoc-meta-hide-face "\n" nil
"lorem ++ ipsum\n" 'no-face
"\n" nil
- "lorem " 'no-face "++" 'adoc-meta-hide-face " ipsum "
'adoc-typewriter-face "++" 'adoc-meta-hide-face "\n" nil
+ "lorem " 'no-face "++" 'adoc-meta-hide-face " ipsum "
'no-face "++" 'adoc-meta-hide-face "\n" nil
"\n" nil
"." 'adoc-meta-face "block ^title" 'adoc-gen-face "\n" nil
@@ -1136,6 +1137,32 @@ on top of the `<mark>' default)."
(adoctest-faces "escaped-passthrough"
"\\" 'adoc-meta-hide-face "+++p+++" 'no-face))
+;;;; Inline passthrough (+text+ / ++text++)
+
+(ert-deftest adoctest-test-inline-passthrough ()
+ ;; +text+ is a passthrough rendered as normal text, not monospace
+ (adoctest-faces "single-plus-passthrough"
+ "+" 'adoc-meta-hide-face "plain" 'no-face "+"
'adoc-meta-hide-face)
+ ;; ++text++ is the unconstrained passthrough
+ (adoctest-faces "double-plus-passthrough"
+ "++" 'adoc-meta-hide-face "un" 'no-face "++"
'adoc-meta-hide-face)
+ ;; inline formatting inside a passthrough is suppressed
+ (adoctest-faces "passthrough-suppresses-formatting"
+ "+" 'adoc-meta-hide-face "no *bold* here" 'no-face
+ "+" 'adoc-meta-hide-face))
+
+(ert-deftest adoctest-test-backtick-still-monospace ()
+ ;; the backtick remains the monospace delimiter (only + changed)
+ (with-temp-buffer
+ (adoc-mode)
+ (insert "x `mono` y")
+ (font-lock-ensure)
+ (goto-char (point-min))
+ (search-forward "mono")
+ (let ((faces (get-text-property (- (point) 2) 'face)))
+ (should (memq 'adoc-typewriter-face
+ (if (listp faces) faces (list faces)))))))
+
;;;; Filling
(ert-deftest adoctest-test-fill-preserves-hard-break ()