branch: elpa/adoc-mode
commit da99827ff7d5ac93a89ed7b1e993349729c17131
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Prefer neocaml-mode for OCaml code blocks, with fallbacks
Map `ocaml' to neocaml-mode, then tuareg-mode, then caml-mode, so source
blocks use the modern tree-sitter mode when it is available and degrade
gracefully otherwise. adoc-code-lang-modes values may now be a single mode
or a list of candidate modes; adoc-get-lang-mode returns the first that is
defined.
---
CHANGELOG.md | 4 ++++
adoc-mode.el | 33 +++++++++++++++++++--------------
test/adoc-mode-font-lock-test.el | 19 +++++++++++++++++++
3 files changed, 42 insertions(+), 14 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 259e2de1ac..eff47a9ea9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## main (unreleased)
+### Changes
+
+- `[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).
+
## 0.9.0 (2026-06-02)
### New features
diff --git a/adoc-mode.el b/adoc-mode.el
index c5c48ae62c..2d1209e032 100644
--- a/adoc-mode.el
+++ b/adoc-mode.el
@@ -274,22 +274,24 @@ are fontified natively regardless of their size."
("ditaa" . artist-mode)
("dot" . fundamental-mode)
("elisp" . emacs-lisp-mode)
- ("ocaml" . tuareg-mode)
+ ("ocaml" . (neocaml-mode tuareg-mode caml-mode))
("screen" . shell-script-mode)
("shell" . sh-mode)
("sqlite" . sql-mode)
)
"Alist mapping languages to their major mode.
-The key is the language name, the value is the major mode. For
-many languages this is simple, but for language where this is not
-the case, this variable provides a way to simplify things on the
-user side. For example, there is no ocaml-mode in Emacs, but the
-mode to use is `tuareg-mode'."
+The key is the language name. The value is either a single major
+mode or a list of candidate modes tried in order, the first
+defined one being used. This makes it possible to map a language
+to a preferred mode with fallbacks: for example there is no
+ocaml-mode in Emacs, so `ocaml' maps to `neocaml-mode', then
+`tuareg-mode', then `caml-mode'."
:group 'adoc
:type '(repeat
(cons
(string "Language name")
- (symbol "Major mode")))
+ (choice (symbol "Major mode")
+ (repeat (symbol "Major mode")))))
:package-version '(adoc-mode . "0.8.0"))
(defcustom adoc-fontify-code-block-default-mode 'prog-mode
@@ -2208,13 +2210,16 @@ TEXTPROPS is an additional plist with textproperties."
(defun adoc-get-lang-mode (lang)
"Return major mode that should be used for LANG.
-LANG is a string, and the returned major mode is a symbol."
- (cl-find-if
- #'fboundp
- (list (cdr (assoc lang adoc-code-lang-modes))
- (cdr (assoc (downcase lang) adoc-code-lang-modes))
- (intern (concat lang "-mode"))
- (intern (concat (downcase lang) "-mode")))))
+LANG is a string, and the returned major mode is a symbol. A
+value in `adoc-code-lang-modes' may be a single mode or a list of
+candidate modes; the first one that is defined is used."
+ (cl-flet ((candidates (value) (if (listp value) value (list value))))
+ (cl-find-if
+ #'fboundp
+ (append (candidates (cdr (assoc lang adoc-code-lang-modes)))
+ (candidates (cdr (assoc (downcase lang) adoc-code-lang-modes)))
+ (list (intern (concat lang "-mode"))
+ (intern (concat (downcase lang) "-mode")))))))
;; Based on `org-src-font-lock-fontify-block' from org-src.el.
(defun adoc-fontify-code-block-natively (lang start-block end-block start-src
end-src)
diff --git a/test/adoc-mode-font-lock-test.el b/test/adoc-mode-font-lock-test.el
index 77c801e68e..b4b6a6e922 100644
--- a/test/adoc-mode-font-lock-test.el
+++ b/test/adoc-mode-font-lock-test.el
@@ -413,6 +413,25 @@
(expect (adoc-test-face-at-range (match-beginning 0) (1- (match-end
0)))
:to-equal '(adoc-verbatim-face adoc-code-face)))))
+ ;; ---- Language -> major mode resolution -----------------------------
+
+ (describe "language mode resolution"
+ (it "uses a single mapped mode"
+ (let ((adoc-code-lang-modes '(("demo" . emacs-lisp-mode))))
+ (expect (adoc-get-lang-mode "demo") :to-equal 'emacs-lisp-mode)))
+
+ (it "tries a list of candidate modes in order, first defined wins"
+ (let ((adoc-code-lang-modes
+ '(("demo" . (adoc-no-such-mode-1 adoc-no-such-mode-2
emacs-lisp-mode)))))
+ (expect (adoc-get-lang-mode "demo") :to-equal 'emacs-lisp-mode)))
+
+ (it "falls back to <lang>-mode when there is no mapping"
+ (expect (adoc-get-lang-mode "emacs-lisp") :to-equal 'emacs-lisp-mode))
+
+ (it "returns nil when no candidate mode is available"
+ (let ((adoc-code-lang-modes '(("demo" . (adoc-no-such-mode-1
adoc-no-such-mode-2)))))
+ (expect (adoc-get-lang-mode "demo") :to-be nil))))
+
;; ---- Character replacements (display overlays) ---------------------
(describe "character replacements"