diff --git a/CHANGELOG.md b/CHANGELOG.md index 74490c0..21c4c0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog]. ## Unreleased +### Breaking changes +* The order of entries in `apheleia-mode-alist` is no longer as + important. Specifically, if two different mode entries in + `apheleia-mode-alist` match the current buffer, then the more + specific one is used, even if it comes later. This is generally + speaking what you would expect to happen. For other cases, such as + ordering of regex entries, or modes versus regexes, order is + respected as before ([#206]). + ### Enhancements * Prettier is now enabled in `svelte-mode`. * More tree-sitter based major modes have been added to diff --git a/apheleia-core.el b/apheleia-core.el index 7d6fb2b..8d63805 100644 --- a/apheleia-core.el +++ b/apheleia-core.el @@ -1016,6 +1016,15 @@ When ARG is not a list its turned into a list." arg (list arg))) +(defun apheleia--get-mode-chain () + "Return list of major modes in current buffer. +This is a list starting with `major-mode' and followed by its +parents, if any." + (let ((modes (list major-mode))) + (while (get (car modes) 'derived-mode-parent) + (push (get (car modes) 'derived-mode-parent) modes)) + (nreverse modes))) + (defun apheleia--get-formatters (&optional interactive) "Return the list of formatters to use for the current buffer. This is a list of symbols that may appear as cars in @@ -1032,14 +1041,45 @@ even if a formatter is configured." (or (and (not (eq interactive 'prompt)) (apheleia--ensure-list (or apheleia-formatter - (cl-dolist (entry apheleia-mode-alist) - (when (or (and (symbolp (car entry)) - (derived-mode-p (car entry))) - (and (stringp (car entry)) - buffer-file-name - (string-match-p - (car entry) buffer-file-name))) - (cl-return (cdr entry))))))) + ;; Go through the mode alist. There are two types of + ;; entries, mode and regex. We should return whichever + ;; entry matches first in the list. However, if two + ;; modes match, then we should return the entry for + ;; the more specific mode. + ;; + ;; Implementation: Iterate once. If we match a regex, + ;; immediately return, unless we already matched a + ;; mode (setting the `formatters' variable), in which + ;; case do not return, but also keep going to see if + ;; there is a more specific mode later in the list. If + ;; we match a mode, save the entry for later + ;; reference, as well as the mode that matched it. + ;; Update that saved entry only when we find a more + ;; specific mode (i.e., a mode that is derived from + ;; but not equal to the previously saved mode). Return + ;; at the end of the loop the saved entry, if we + ;; didn't exit early. + (let* ((unset (make-symbol "gensym-unset")) + (matched-mode nil) + (formatters unset)) + (cl-dolist (entry apheleia-mode-alist + (unless (eq formatters unset) + formatters)) + (when (and (stringp (car entry)) + buffer-file-name + (string-match-p + (car entry) buffer-file-name) + (eq formatters unset)) + (cl-return (cdr entry))) + (when (and (symbolp (car entry)) + (derived-mode-p (car entry)) + (or (eq formatters unset) + (and + (not (eq (car entry) matched-mode)) + (provided-mode-derived-p + (car entry) matched-mode)))) + (setq matched-mode (car entry)) + (setq formatters (cdr entry)))))))) (and interactive (list (intern diff --git a/apheleia.el b/apheleia.el index 033fe1a..2ae9a88 100644 --- a/apheleia.el +++ b/apheleia.el @@ -215,12 +215,7 @@ rather than using this system." :group 'apheleia) (defcustom apheleia-mode-alist - '(;; php-mode has to come before cc-mode - (php-mode . phpcs) - ;; json-mode has to come before javascript-mode (aka js-mode) - (json-mode . prettier-json) - (json-ts-mode . prettier-json) - ;; rest are alphabetical + '(;; Alphabetical please (asm-mode . asmfmt) (awk-mode . gawk) (bash-ts-mode . shfmt) @@ -257,6 +252,8 @@ rather than using this system." (js3-mode . prettier-javascript) (js-mode . prettier-javascript) (js-ts-mode . prettier-javascript) + (json-mode . prettier-json) + (json-ts-mode . prettier-json) (kotlin-mode . ktlint) (latex-mode . latexindent) (LaTeX-mode . latexindent) @@ -265,6 +262,7 @@ rather than using this system." (nasm-mode . asmfmt) (nix-mode . nixfmt) (perl-mode . perltidy) + (php-mode . phpcs) (purescript-mode . purs-tidy) (python-mode . black) (python-ts-mode . black) @@ -302,8 +300,10 @@ Be careful when writing regexps to include \"\\'\" and to escape to match \".jsx\" files you might use \"\\.jsx\\'\". If a given mode derives from another mode (e.g. `php-mode' and -`cc-mode'), then ensure that the deriving mode comes before the mode -to derive from, as the list is interpreted sequentially." +`cc-mode'), then whichever entry in the alist is more specific +will apply. In the case that multiple modes match +`derived-mode-p' for the current buffer but neither derives from +the other, whichever entry comes first will be used." :type '(alist :key-type (choice (symbol :tag "Major mode")