branch: elpa/adoc-mode
commit 2d0e3c2ad6b64ecc0645416d957ce28d8ef35a9e
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Highlight backslash-escaped inline formatting
A backslash before an inline formatting delimiter escapes it in
AsciiDoc, so the markup should render literally. Add a high-priority
font-lock keyword that de-emphasises the backslash and marks the
escaped delimiter run as reserved literal text, so the quote keywords
skip it. This fixes the case where an escaped unconstrained span
(\**x**) still leaked an inner constrained match.
---
CHANGELOG.md | 1 +
README.adoc | 5 +++--
adoc-mode.el | 26 ++++++++++++++++++++++++++
test/adoc-mode-test.el | 23 +++++++++++++++++++++++
4 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a28584aafd..14136b0a6a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
### New features
+- Honour backslash escapes in inline formatting: a backslash before a
formatting delimiter (e.g. `\*not bold*`, `\**nor this**`, `` \`nor code` ``)
now de-emphasises the backslash and leaves the escaped span as literal text
instead of fontifying it as markup. Previously the unconstrained forms still
leaked an inner constrained match (`\**x**` highlighted `x`).
- Add region-aware text-styling commands under the `C-c C-s` prefix, modelled
on `markdown-mode`: `adoc-insert-bold` (`C-c C-s b`, `*text*`),
`adoc-insert-italic` (`i`, `_text_`), `adoc-insert-monospace` (`m`, `` `text`
``), `adoc-insert-highlight` (`h`, `#text#`), `adoc-insert-superscript` (`^`,
`^text^`), `adoc-insert-subscript` (`~`, `~text~`), and `adoc-insert-link`
(`l`). Each wraps the active region or the word at point, removes the markup
again when it is already wrapped, and inse [...]
- Add outline cycling commands modelled on `org-mode` and `markdown-mode`:
`adoc-cycle` (`TAB`) rotates the visibility of the section subtree at point
(folded / child titles / fully shown) when point is on a one-line title, and
otherwise indents as usual; `adoc-cycle-buffer` (`S-TAB`) rotates the whole
buffer between overview, contents, and show-all. Both build on the
`outline-cycle` / `outline-cycle-buffer` primitives.
- Add list editing: `adoc-promote` (`M-left`) and `adoc-demote` (`M-right`)
now nest the list item at point one level deeper or shallower (for unordered
`*`/`-` and implicitly-numbered `.` lists) in addition to acting on section
titles, and the new `adoc-insert-list-item` (`M-RET`) inserts a sibling item
below the current one, keeping its indentation and marker and incrementing the
number/letter of explicitly-numbered items.
diff --git a/README.adoc b/README.adoc
index 1d253115fb..7c43a20370 100644
--- a/README.adoc
+++ b/README.adoc
@@ -34,7 +34,7 @@ be easily ignored.
Here are some of the main features of `adoc-mode`:
-- comprehensive syntax highlighting for all AsciiDoc elements
+- comprehensive syntax highlighting for all AsciiDoc elements, including
backslash-escaped inline formatting (e.g. `\*not bold*`)
- native fontification of source code blocks using language-appropriate major
modes
- font-lock support for Asciidoctor inline macros (`kbd:[]`, `btn:[]`,
`menu:[]`, etc.)
- inline image preview with right-click context menus and remote image support
@@ -234,7 +234,8 @@ These days all upstream packages (e.g. on MELPA) are built
from Bozhidar's fork.
Here are some features that we're considering to add in the future:
-- Correctly highlighting backslash escapes
+- Table editing and alignment
+- Verifying and improving compliance with the latest AsciiDoc language
specification
Check out the issue tracker for more details.
diff --git a/adoc-mode.el b/adoc-mode.el
index c3001eda4e..bcf82428e9 100644
--- a/adoc-mode.el
+++ b/adoc-mode.el
@@ -1955,6 +1955,28 @@ 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
+(defconst adoc-re-escaped-formatting
+ "\\(\\\\\\)\\(\\([*_`#+^~]\\)\\3*\\)"
+ "Regexp matching a backslash-escaped inline formatting delimiter.
+Group 1 is the escaping backslash; group 2 is the escaped
+delimiter run, a maximal run of one formatting delimiter
+character (`*', `**', `+++', …). A lone `$' is intentionally
+excluded: single dollars are not markup (only `$$' is), and a
+literal `\\$' is far more common in prose.")
+
+(defun adoc-kw-escaped-formatting ()
+ "Return a font-lock keyword for backslash-escaped inline formatting.
+In AsciiDoc a backslash before an inline formatting delimiter
+escapes it, so the markup is rendered literally. The keyword
+de-emphasises the backslash and marks the escaped delimiters as
+reserved literal text, so the quote keywords (which refuse to
+match across `adoc-reserved' text) leave the span alone."
+ (list
+ `(lambda (end)
+ (adoc-kwf-std end adoc-re-escaped-formatting '(2)))
+ '(1 '(face adoc-meta-hide-face adoc-reserved t) t) ; the backslash
+ '(2 '(face nil adoc-reserved t) t))) ; the escaped delimiters
+
(defun adoc-kw-inline-macro (&optional cmd-name unconstrained
attribute-list-constraints cmd-face target-faces target-meta-p attribute-list
textprops)
"Returns a kewyword which highlights an inline macro.
@@ -2573,6 +2595,10 @@ for multiline constructs to be matched."
;; 7. Replacements2
+ ;; backslash-escaped inline formatting (must precede the quote keywords
+ ;; below so the escaped delimiters are reserved before they are scanned)
+ (adoc-kw-escaped-formatting)
+
;; Inline code and passthroughs
;; ----------------------------
;; Asciidoctor: `text` is inline code (monospace literal)
diff --git a/test/adoc-mode-test.el b/test/adoc-mode-test.el
index d0135005b6..42c4d6d89d 100644
--- a/test/adoc-mode-test.el
+++ b/test/adoc-mode-test.el
@@ -1113,6 +1113,29 @@ on top of the `<mark>' default)."
(adoc-insert-link "https://x.com" "here")
(should (string-equal (buffer-string) "see https://x.com[here] now"))))
+;;;; Escaped formatting
+
+(ert-deftest adoctest-test-escaped-formatting ()
+ ;; constrained: escaped markup is literal, the backslash is de-emphasised
+ (adoctest-faces "escaped-bold"
+ "\\" 'adoc-meta-hide-face "*foo*" 'no-face)
+ ;; unconstrained escape (previously leaked an inner constrained match)
+ (adoctest-faces "escaped-unconstrained-bold"
+ "\\" 'adoc-meta-hide-face "**foo**" 'no-face)
+ (adoctest-faces "escaped-monospace"
+ "\\" 'adoc-meta-hide-face "`foo`" 'no-face)
+ ;; a backslash before a non-formatting char is left untouched
+ (adoctest-faces "non-escape-backslash" "a\\b" 'no-face)
+ ;; normal markup after an escaped span still fontifies
+ (adoctest-faces "escape-then-bold"
+ "\\" 'adoc-meta-hide-face "*x* " 'no-face
+ "*" 'adoc-meta-hide-face "y" 'adoc-bold-face "*"
'adoc-meta-hide-face)
+ ;; a lone dollar is not markup, so a literal \$ is left untouched
+ (adoctest-faces "escaped-dollar" "x \\$5 y" 'no-face)
+ ;; a triple-delimiter passthrough is escaped in full
+ (adoctest-faces "escaped-passthrough"
+ "\\" 'adoc-meta-hide-face "+++p+++" 'no-face))
+
;;;; List editing
(ert-deftest adoctest-test-list-promote-demote-unordered ()