aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRadon Rosborough <radon@intuitiveexplanations.com>2024-05-17 15:28:50 -0700
committerGitHub <noreply@github.com>2024-05-17 15:28:50 -0700
commit61766b50b24fa16be519d77795dc63522e04dce8 (patch)
tree074fd06c9dec07e62e9ee76abe22259fb1487611
parent66bf5195b4e922f23a9d573f2823daeb63e7ed5b (diff)
[#302] User option: apheleia-mode-predicates (#303)
For https://github.com/radian-software/apheleia/issues/302. Not tested yet. Going to add unit tests before merging.
-rw-r--r--CHANGELOG.md8
-rw-r--r--apheleia-formatters.el36
-rw-r--r--test/unit/apheleia-unit-test.el50
3 files changed, 92 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f03780b..3d7a553 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,13 @@ The format is based on [Keep a Changelog].
backwards compatibility, and errors can also be reported by
throwing, as normal. Implemented in [#204].
+### Features
+* New user option `apheleia-mode-predicates`. The default value
+ handles `mhtml-mode` correctly by always using whatever formatter
+ you have configured for that mode, rather than using `css-mode`,
+ `html-mode`, etc formatters depending on the position of point
+ ([#302]).
+
### Enhancements
* There is a new keyword argument to `apheleia-format-buffer` which is
a more powerful callback that is guaranteed to be called except in
@@ -42,6 +49,7 @@ The format is based on [Keep a Changelog].
[#286]: https://github.com/radian-software/apheleia/pull/286
[#285]: https://github.com/radian-software/apheleia/issues/285
[#290]: https://github.com/radian-software/apheleia/pull/290
+[#302]: https://github.com/radian-software/apheleia/issues/302
## 4.1 (released 2024-02-25)
### Enhancements
diff --git a/apheleia-formatters.el b/apheleia-formatters.el
index a96272b..6257e09 100644
--- a/apheleia-formatters.el
+++ b/apheleia-formatters.el
@@ -405,6 +405,32 @@ mode."
(symbol :tag "Formatter"))))
:group 'apheleia)
+(defun apheleia-mhtml-mode-predicate ()
+ "Return `mhtml-mode' if the user is in that mode.
+This checks text properties because `mhtml-mode' sets
+`major-mode' to different values depending on where the user is
+in the buffer."
+ (when (get-text-property
+ (if (and (eobp) (not (bobp)))
+ (1- (point))
+ (point))
+ 'mhtml-submode)
+ #'mhtml-mode))
+
+;;;###autoload
+(defcustom apheleia-mode-predicates '(apheleia-mhtml-mode-predicate)
+ "List of predicates that check for sneaky major modes.
+Sometimes a major mode will set `major-mode' to something other
+than itself, making it hard to correctly detect what major mode
+is active. In such cases you can add a predicate to this list to
+handle it. Predicates take no arguments, are run in the current
+buffer, and should return the name of a mode if one is detected.
+If all the predicates return nil, or if there aren't any in the
+list, then only the value of `major-mode' is used to determine
+the major mode. The detected major mode affects the selection
+from `apheleia-mode-alist'."
+ :type '(repeat function)
+ :group 'apheleia)
(defcustom apheleia-formatter-exited-hook nil
"Abnormal hook run after a formatter has finished running.
@@ -1240,6 +1266,7 @@ the current buffer.
Consult the values of `apheleia-mode-alist' and
`apheleia-formatter' to determine which formatter is configured.
+Consult also `apheleia-mode-predicates', if non-nil.
If INTERACTIVE is non-nil, then prompt the user for which
formatter to run if none is configured, instead of returning nil.
@@ -1268,7 +1295,12 @@ even if a formatter is configured."
;; didn't exit early.
(let* ((unset (make-symbol "gensym-unset"))
(matched-mode nil)
- (formatters unset))
+ (formatters unset)
+ (mode major-mode))
+ (cl-dolist (pred apheleia-mode-predicates)
+ (when-let ((new-mode (funcall pred)))
+ (setq mode new-mode)
+ (cl-return)))
(cl-dolist (entry apheleia-mode-alist
(unless (eq formatters unset)
formatters))
@@ -1279,7 +1311,7 @@ even if a formatter is configured."
(eq formatters unset))
(cl-return (cdr entry)))
(when (and (symbolp (car entry))
- (derived-mode-p (car entry))
+ (provided-mode-derived-p mode (car entry))
(or (eq formatters unset)
(and
(not (eq (car entry) matched-mode))
diff --git a/test/unit/apheleia-unit-test.el b/test/unit/apheleia-unit-test.el
index c5d54cc..da49005 100644
--- a/test/unit/apheleia-unit-test.el
+++ b/test/unit/apheleia-unit-test.el
@@ -95,3 +95,53 @@
"solves issue #290"
(" | <div class=\"left-[40rem] fixed inset-y-0 right-0 z-0 hidden lg:block xl:left-[50rem]\">\n <svg\n"
"|<div class=\"left-[40rem] fixed inset-y-0 right-0 z-0 hidden lg:block xl:left-[50rem]\">\n <svg")))))
+
+(describe "apheleia--get-formatters"
+ (cl-macrolet ((testcases
+ (description mode-alist pred-list &rest specs)
+ `(cl-flet ((fmt-error
+ (mode fname expected)
+ (with-temp-buffer
+ (setq major-mode mode)
+ (setq-local buffer-file-name fname)
+ (let ((real (apheleia--get-formatters)))
+ (unless (equal real expected)
+ real)))))
+ (it ,description
+ ,@(mapcar
+ (lambda (spec)
+ `(let ((apheleia-mode-alist ,mode-alist)
+ (apheleia-mode-predicates ,pred-list))
+ (expect
+ (fmt-error ,@spec)
+ :to-be nil)))
+ specs)))))
+ (testcases
+ "always returns nil when user options are nil"
+ nil nil
+ ('text-mode "foo.txt" nil)
+ ('fundamental-mode nil nil)
+ ('cc-mode "foo.c" nil)
+ ('mhtml-mode "foo.html" nil))
+ (testcases
+ "selects based on mode, filename, and predicates"
+ '(("\\.foobar\\'" . fmt-foobar)
+ (sgml-mode . fmt-sgml)
+ (html-mode . (fmt-html fmt-html-again))
+ (text-mode . fmt-text)
+ (blah-mode . fmt-blah)
+ ("\\.foobaz\\'" . fmt-foobaz))
+ '((lambda ()
+ (when (and buffer-file-name
+ (string-match-p "\\.blah" buffer-file-name))
+ 'blah-mode)))
+ ('fundamental-mode nil nil)
+ ('fundamental-mode "ok.foobar" '(fmt-foobar))
+ ('text-mode nil '(fmt-text))
+ ('sgml-mode nil '(fmt-sgml))
+ ('html-mode nil '(fmt-html fmt-html-again))
+ ('html-mode "ok.foobar" '(fmt-foobar))
+ ('html-mode "ok.foobaz" '(fmt-html fmt-html-again))
+ ('html-mode "ok.blah" '(fmt-blah))
+ ('html-mode "ok.blah.foobar" '(fmt-foobar))
+ ('html-mode "ok.blah.foobaz" '(fmt-blah)))))