branch: elpa/editorconfig
commit 946c09fcdd8bece5fbb52d01c22c4649f50ebcad
Author: monnier <[email protected]>
Commit: GitHub <[email protected]>

    New variable `editorconfig-indent-size-vars` (#383)
    
    This commit aligns indentation var rules with those of Emacs-30
    
    This introduces a new var `editorconfig-indent-size-vars` to determine
    how to set which variable for the current major mode.
    For modes which don't set this var, the default value
    still looks up `editorconfig-indentation-alist` but the
    documented format of that var is changed in two ways:
    
    - For `(MODE . FUNCTION)`, FUNCTION should now return the settings
      as a list rather than apply the settings directly.
    - For `(MODE . INDENT-SPEC-LIST)`, the elements in the list cannot
      be cons cells any more, only variable names.
    
    The old format elements are still supported but a warning
    message is emitted when we encounter them.
    
    In addition, if no entry is found in `editorconfig-indentation-alist`
    the default now tries to guess an appropriate variable name, by
    trying out a few common naming conventions.
    
    * editorconfig.el (editorconfig-indent-size-vars): New var.
    (editorconfig--default-indent-size-function): New function.
    (editorconfig--get-indentation): New function.
    (editorconfig-set-indentation): Rewrite using it.
    (editorconfig--get-indentation-web-mode)
    (editorconfig--get-indentation-nxml-mode): New functions.
    (editorconfig--get-indentation-(python|latex|lisp)-mode):
    Rename from `editorconfig-set-indentation-*-mode` and make them return
    a list of settings instead of setting the vars directly.
    (editorconfig-indentation-alist): Remove entries for modes that follow
    a common enough naming convention that
    `editorconfig--default-indent-size-function` is able to guess the
    relevant var's name.
    Use the new `editorconfig--get-*` functions.
    Adjust docstring to the new format.
    
    Co-authored-by: Hong Xu <[email protected]>
---
 editorconfig.el | 305 +++++++++++++++++++++++++++++---------------------------
 1 file changed, 160 insertions(+), 145 deletions(-)

diff --git a/editorconfig.el b/editorconfig.el
index 352a0d3f28b..f82f49d703e 100644
--- a/editorconfig.el
+++ b/editorconfig.el
@@ -172,7 +172,6 @@ This hook will be run even when there are no matching 
sections in
   ;; For contributors: Sort modes in alphabetical order
   '((ada-mode ada-indent)
     (ada-ts-mode ada-ts-mode-indent-offset)
-    (apache-mode apache-indent-level)
     (awk-mode c-basic-offset)
     (bash-ts-mode sh-basic-offset
                   sh-indentation)
@@ -180,26 +179,19 @@ This hook will be run even when there are no matching 
sections in
     (c++-mode c-basic-offset)
     (c++-ts-mode c-basic-offset
                  c-ts-mode-indent-offset)
-    (c-mode c-basic-offset)
     (c-ts-mode c-basic-offset
                c-ts-mode-indent-offset)
     (cmake-mode cmake-tab-width)
     (cmake-ts-mode cmake-tab-width
                    cmake-ts-mode-indent-offset)
     (coffee-mode coffee-tab-width)
-    (cperl-mode cperl-indent-level)
-    (crystal-mode crystal-indent-level)
     (csharp-mode c-basic-offset)
     (csharp-ts-mode c-basic-offset
                     csharp-ts-mode-indent-offset)
-    (css-mode css-indent-offset)
     (css-ts-mode css-indent-offset)
     (d-mode c-basic-offset)
     (elixir-ts-mode elixir-ts-indent-offset)
-    (emacs-lisp-mode . editorconfig-set-indentation-lisp-mode)
-    (enh-ruby-mode enh-ruby-indent-level)
-    (erlang-mode erlang-indent-level)
-    (ess-mode ess-indent-offset)
+    (emacs-lisp-mode . editorconfig--get-indentation-lisp-mode)
     (f90-mode f90-associate-indent
               f90-continuation-indent
               f90-critical-indent
@@ -212,12 +204,9 @@ This hook will be run even when there are no matching 
sections in
     (fsharp-mode fsharp-continuation-offset
                  fsharp-indent-level
                  fsharp-indent-offset)
-    (gdscript-mode gdscript-indent-offset)
     (go-ts-mode go-ts-mode-indent-offset)
     (gpr-mode gpr-indent)
     (gpr-ts-mode gpr-ts-mode-indent-offset)
-    (graphql-mode graphql-indent-level)
-    (groovy-mode groovy-indent-offset)
     (haskell-mode haskell-indent-spaces
                   haskell-indent-offset
                   haskell-indentation-layout-offset
@@ -227,43 +216,31 @@ This hook will be run even when there are no matching 
sections in
                   haskell-indentation-where-pre-offset
                   shm-indent-spaces)
     (haxor-mode haxor-tab-width)
-    (hcl-mode hcl-indent-level)
     (html-ts-mode html-ts-mode-indent-offset)
     (idl-mode c-basic-offset)
     (jade-mode jade-tab-width)
     (java-mode c-basic-offset)
     (java-ts-mode c-basic-offset
                   java-ts-mode-indent-offset)
-    (js-mode js-indent-level)
     (js-ts-mode js-indent-level)
     (js-json-mode js-indent-level)
     (js-jsx-mode js-indent-level sgml-basic-offset)
-    (js2-mode js2-basic-offset)
     (js2-jsx-mode js2-basic-offset sgml-basic-offset)
-    (js3-mode js3-indent-level)
     (json-mode js-indent-level)
     (json-ts-mode json-ts-mode-indent-offset)
     (jsonian-mode jsonian-default-indentation)
-    (julia-mode julia-indent-offset)
     (kotlin-mode kotlin-tab-width)
     (kotlin-ts-mode kotlin-ts-mode-indent-offset)
-    (latex-mode . editorconfig-set-indentation-latex-mode)
-    (lisp-mode . editorconfig-set-indentation-lisp-mode)
+    (latex-mode . editorconfig--get-indentation-latex-mode)
+    (lisp-mode . editorconfig--get-indentation-lisp-mode)
     (livescript-mode livescript-tab-width)
-    (lua-mode lua-indent-level)
-    (lua-ts-mode lua-ts-indent-offset)
-    (magik-mode magik-indent-level)
     (magik-ts-mode magik-indent-level)
-    (matlab-mode matlab-indent-level)
     (meson-mode meson-indent-basic)
     (mips-mode mips-tab-width)
     (mustache-mode mustache-basic-offset)
-    (nasm-mode nasm-basic-offset)
-    (nginx-mode nginx-indent-level)
-    (nxml-mode nxml-child-indent (nxml-attribute-indent . 2))
+    (nxml-mode . editorconfig--get-indentation-nxml-mode)
     (objc-mode c-basic-offset)
     (octave-mode octave-block-offset)
-    (perl-mode perl-indent-level)
     ;; No need to change `php-mode-coding-style' value for php-mode
     ;; since we run editorconfig later than it resets `c-basic-offset'.
     ;; See https://github.com/editorconfig/editorconfig-emacs/issues/116
@@ -274,30 +251,20 @@ This hook will be run even when there are no matching 
sections in
     (protobuf-mode c-basic-offset)
     (ps-mode ps-mode-tab)
     (pug-mode pug-tab-width)
-    (puppet-mode puppet-indent-level)
-    (python-mode . editorconfig-set-indentation-python-mode)
-    (python-ts-mode . editorconfig-set-indentation-python-mode)
+    (python-mode . editorconfig--get-indentation-python-mode)
+    (python-ts-mode . editorconfig--get-indentation-python-mode)
     (rjsx-mode js-indent-level sgml-basic-offset)
-    (ruby-mode ruby-indent-level)
     (ruby-ts-mode ruby-indent-level)
-    (rust-mode rust-indent-offset)
     (rust-ts-mode rust-indent-offset
                   rust-ts-mode-indent-offset)
-    (rustic-mode rustic-indent-offset)
     (scala-mode scala-indent:step)
     (scss-mode css-indent-offset)
-    (sgml-mode sgml-basic-offset)
     (sh-mode sh-basic-offset sh-indentation)
-    (slim-mode slim-indent-offset)
-    (sml-mode sml-indent-level)
-    (svelte-mode svelte-basic-offset)
     (swift-mode swift-mode:basic-offset)
-    (terra-mode terra-indent-level)
     (tcl-mode tcl-indent-level
               tcl-continued-indent-level)
     (templ-ts-mode go-ts-mode-indent-offset js-indent-level)
     (toml-ts-mode toml-ts-mode-indent-offset)
-    (typescript-mode typescript-indent-level)
     (typescript-ts-base-mode typescript-ts-mode-indent-offset)
     (verilog-mode verilog-indent-level
                   verilog-indent-level-behavioral
@@ -305,43 +272,21 @@ This hook will be run even when there are no matching 
sections in
                   verilog-indent-level-module
                   verilog-cexp-indent
                   verilog-case-indent)
-    (web-mode (web-mode-indent-style . (lambda (size) 2))
-              web-mode-attr-indent-offset
-              web-mode-attr-value-indent-offset
-              web-mode-code-indent-offset
-              web-mode-css-indent-offset
-              web-mode-markup-indent-offset
-              web-mode-sql-indent-offset
-              web-mode-block-padding
-              web-mode-script-padding
-              web-mode-style-padding)
-    (yaml-mode yaml-indent-offset)
-    (yaml-ts-mode yaml-indent-offset)
-    (zig-mode zig-indent-offset))
+    (web-mode . editorconfig--get-indentation-web-mode)
+    (yaml-ts-mode yaml-indent-offset))
   "Alist of indentation setting methods by modes.
 
-Each element looks like (MODE . FUNCTION) or (MODE . INDENT-SPEC-LIST).
+This is a fallback used for those modes which don't set
+`editorconfig-indent-size-vars'.
 
-If FUNCTION is provided, it will be called when setting the
-indentation.  The indent size will be passed.
+Each element looks like (MODE . FUNCTION) or (MODE . VARLIST).
 
-If INDENT-SPEC-LIST is provided, each element of it must have one of the
-following forms:
+If FUNCTION is provided, it will be called with one argument (the indent size)
+when setting the indentation.  It should return the list of settings that need
+to be applied, where each setting has the form (VARIABLE . VALUE).
 
- 1. VARIABLE
-    It means (VARIABLE . 1).
-
- 2. (VARIABLE . SPEC)
-    Setting VARIABLE according to the type of SPEC:
-
-      - Integer
-        The value is (* SPEC INDENT-SIZE);
-
-      - Function
-        The value is (funcall SPEC INDENT-SIZE);
-
-      - Any other type.
-        The value is SPEC.
+If VARLIST is provided, each element of VARLIST should be the name of
+a variable that we should set to the indent size.
 
 NOTE: Only the **buffer local** value of VARIABLE will be set."
   :type '(alist :key-type symbol :value-type sexp)
@@ -427,35 +372,46 @@ Make a message by passing ARGS to `format-message'."
   (and (stringp string)
        (string-match-p "\\`[0-9]+\\'" string)))
 
-(defun editorconfig-set-indentation-python-mode (size)
-  "Set `python-mode' indent size to SIZE."
-  (when (boundp 'python-indent-offset)
-    (setq-local python-indent-offset size))
-  ;; For https://gitlab.com/python-mode-devs/python-mode
-  (when (boundp 'py-indent-offset)
-    (setq-local py-indent-offset size)))
-
-(defun editorconfig-set-indentation-latex-mode (size)
-  "Set `latex-mode' indent size to SIZE."
-  (setq-local tex-indent-basic size)
-  (setq-local tex-indent-item size)
-  (setq-local tex-indent-arg (* 2 size))
-  ;; For AUCTeX
-  (when (boundp 'TeX-brace-indent-level)
-    (setq-local TeX-brace-indent-level size))
-  (when (boundp 'LaTeX-indent-level)
-    (setq-local LaTeX-indent-level size))
-  (when (boundp 'LaTeX-item-indent)
-    (setq-local LaTeX-item-indent (- size))))
-
-(defun editorconfig-set-indentation-lisp-mode (size)
- "Set indent size to SIZE for Lisp mode(s)."
- (when (cond ((null editorconfig-lisp-use-default-indent)  t)
-             ((eql t editorconfig-lisp-use-default-indent) nil)
-             ((numberp editorconfig-lisp-use-default-indent)
-              (not (eql size editorconfig-lisp-use-default-indent)))
-             (t t))
-   (setq-local lisp-indent-offset size)))
+(defun editorconfig--get-indentation-web-mode (size)
+  `((web-mode-indent-style . 2)
+    (web-mode-attr-indent-offset       . ,size)
+    (web-mode-attr-value-indent-offset . ,size)
+    (web-mode-code-indent-offset       . ,size)
+    (web-mode-css-indent-offset        . ,size)
+    (web-mode-markup-indent-offset     . ,size)
+    (web-mode-sql-indent-offset        . ,size)
+    (web-mode-block-padding            . ,size)
+    (web-mode-script-padding           . ,size)
+    (web-mode-style-padding            . ,size)))
+
+(defun editorconfig--get-indentation-python-mode (size)
+  "Vars to set `python-mode' indent size to SIZE."
+  `((python-indent-offset . ,size)      ;For python.el
+    (py-indent-offset . ,size)))        ;For python-mode.el
+
+(defun editorconfig--get-indentation-latex-mode (size)
+  "Vars to set `latex-mode' indent size to SIZE."
+  `((tex-indent-basic . ,size)
+    (tex-indent-item . ,size)
+    (tex-indent-arg . ,(* 2 size))
+    ;; For AUCTeX
+    (TeX-brace-indent-level . ,size)
+    (LaTeX-indent-level . ,size)
+    (LaTeX-item-indent . ,(- size))))
+
+(defun editorconfig--get-indentation-nxml-mode (size)
+  "Vars to set `nxml-mode' indent size to SIZE."
+  `((nxml-child-indent . ,size)
+    (nxml-attribute-indent . ,(* 2 size))))
+
+(defun editorconfig--get-indentation-lisp-mode (size)
+  "Set indent size to SIZE for Lisp mode(s)."
+  (when (cond ((null editorconfig-lisp-use-default-indent)  t)
+              ((eql t editorconfig-lisp-use-default-indent) nil)
+              ((numberp editorconfig-lisp-use-default-indent)
+               (not (eql size editorconfig-lisp-use-default-indent)))
+              (t t))
+    `((lisp-indent-offset . ,size))))
 
 (cl-defun editorconfig--should-set (symbol)
   "Determine if editorconfig should set SYMBOL."
@@ -475,58 +431,117 @@ Make a message by passing ARGS to `format-message'."
 
   t)
 
-(defun editorconfig-set-indentation (style &optional size tab_width)
-  "Set indentation type from STYLE, SIZE and TAB_WIDTH."
+(defvar editorconfig-indent-size-vars
+  #'editorconfig--default-indent-size-function
+  "Rule to use to set a given `indent_size'.
+This should hold the list of variables that need to be set to SIZE
+to tell the indentation code of the current major mode to use a basic
+indentation step of size SIZE.
+It can also take the form of a function, in which case we call it with
+a single SIZE argument (an integer) and it should return a list
+of (VAR . VAL) pairs indicating the variables to set and the values to
+set them to.
+Major modes are expected to set this buffer-locally.")
+
+(defun editorconfig--default-indent-size-function (size)
+ "Guess which variables to set to for the indentation step to have size SIZE.
+This relies on `editorconfig-indentation-alist' supplemented with a crude
+heuristic for those modes not found there."
+  (let ((parents (if (fboundp 'derived-mode-all-parents) ;Emacs-30
+                     (derived-mode-all-parents major-mode)
+                   (let ((modes nil)
+                         (mode major-mode))
+                     (while mode
+                       (push mode modes)
+                       (setq mode (get mode 'derived-mode--parent)))
+                     (nreverse modes))))
+        entry)
+    (let ((parents parents))
+      (while (and parents (not entry))
+        (setq entry (assq (pop parents) editorconfig-indentation-alist))))
+    (or
+     (when entry
+       (let ((rule (cdr entry)))
+         ;; Filter out settings of unknown vars.
+         (delq nil
+               (mapcar
+                (lambda (elem)
+                  (let ((v (car elem)))
+                    (cond
+                     ((not (symbolp v))
+                      (message "Unsupported element in 
`editorconfig-indentation-alist': %S" elem))
+                     ((boundp v) elem))))
+                (if (functionp rule)
+                    (let ((result (funcall rule size)))
+                      (if (and (consp (car-safe result))
+                               (boundp (caar result)))
+                          result
+                        (message "Invalid return value for: %S"
+                                 rule)
+                        nil))
+                  (mapcar
+                   (lambda (elem)
+                     (cond
+                      ((symbolp elem) `(,elem . ,size))
+                      ((consp elem)
+                       (message "Old-style entry in 
editorconfig-indentation-alist: %S" elem)
+                       `(,(car elem)
+                         . ,(let ((spec (cdr elem)))
+                              (cond ((functionp spec) (funcall spec size))
+                                    ((integerp spec) (* spec size))
+                                    (t spec)))))))
+                   rule))))))
+     ;; Fallback, let's try and guess.
+     (let ((suffixes '("-indent-level" "-basic-offset" "-indent-offset"))
+           (guess ()))
+       (while (and parents (not guess))
+         (let* ((mode (pop parents))
+                (modename (symbol-name mode))
+                (name (substring modename 0
+                                 (string-match "-mode\\'" modename))))
+           (dolist (suffix suffixes)
+             (let ((sym (intern-soft (concat name suffix))))
+               (when (and sym (boundp sym))
+                 (setq guess sym))))))
+       (when guess `((,guess . ,size))))
+     (and (local-variable-p 'smie-rules-function)
+          `((smie-indent-basic . ,size))))))
+
+(defun editorconfig--get-indentation (style &optional size tab_width)
+  "Get indentation vars according to STYLE, SIZE, and TAB_WIDTH."
+  (if tab_width (setq tab_width (string-to-number tab_width)))
+
   (setq size
         (cond ((editorconfig-string-integer-p size)
                (string-to-number size))
               ((equal size "tab")
-               "tab")
+               (or tab_width tab-width))
               (t
                nil)))
 
-  (cond ((not (editorconfig--should-set 'tab-width))
-         nil)
-        (tab_width
-         (setq tab-width (string-to-number tab_width)))
-        ((numberp size)
-         (setq tab-width size)))
-
-  (when (equal size "tab")
-    (setq size tab-width))
-
-  (cond ((not (editorconfig--should-set 'indent-tabs-mode))
-         nil)
-        ((equal style "space")
-         (setq indent-tabs-mode nil))
-        ((equal style "tab")
-         (setq indent-tabs-mode t)))
-
-  (when size
-    (when (and (featurep 'evil)
-               (editorconfig--should-set 'evil-shift-width))
-      (setq-local evil-shift-width size))
-    (let ((parent major-mode)
-          entry)
-      ;; Find the closet parent mode of `major-mode' in
-      ;; `editorconfig-indentation-alist'.
-      (while (and (not (setq entry (assoc parent 
editorconfig-indentation-alist)))
-                  (setq parent (get parent 'derived-mode-parent))))
-      (when entry
-        (let ((fn-or-list (cdr entry)))
-          (cond ((functionp fn-or-list) (funcall fn-or-list size))
-                ((listp fn-or-list)
-                 (dolist (elem fn-or-list)
-                   (cond ((and (symbolp elem)
-                               (editorconfig--should-set elem))
-                          (set (make-local-variable elem) size))
-                         ((and (consp elem)
-                               (editorconfig--should-set (car elem)))
-                          (let ((spec (cdr elem)))
-                            (set (make-local-variable (car elem))
-                                 (cond ((functionp spec) (funcall spec size))
-                                       ((integerp spec) (* spec size))
-                                       (t spec))))))))))))))
+  `(,@(cond (tab_width `((tab-width . ,tab_width)))
+            ((numberp size) `((tab-width . ,size))))
+
+    ,@(pcase style
+        ("space" `((indent-tabs-mode . nil)))
+        ("tab" `((indent-tabs-mode . t))))
+
+    ,@(when (and size (featurep 'evil))
+        `((evil-shift-width . ,size)))
+
+    ,@(cond
+       ((null size) nil)
+       ((functionp editorconfig-indent-size-vars)
+        (funcall editorconfig-indent-size-vars size))
+       (t (mapcar (lambda (v) `(,v . ,size))
+                  editorconfig-indent-size-vars)))))
+
+
+(defun editorconfig-set-indentation (style &optional size tab_width)
+  "Set indentation type from STYLE, SIZE and TAB_WIDTH."
+  (dolist (setting (editorconfig--get-indentation style size tab_width))
+    (when (editorconfig--should-set (car setting))
+      (set (make-local-variable (car setting)) (cdr setting)))))
 
 (defvar-local editorconfig--apply-coding-system-currently nil
   "Used internally.")

Reply via email to