branch: scratch/editorconfig commit 1dabfc65754bbedd14435f6e34b275b629c417f8 Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Stefan Monnier <monn...@iro.umontreal.ca>
WiP --- editorconfig.el | 182 +++++++++++++++++++++++----------- ert-tests/editorconfig-core-handle.el | 2 +- ert-tests/editorconfig-core.el | 2 +- ert-tests/editorconfig-fnmatch.el | 6 +- ert-tests/editorconfig.el | 2 +- 5 files changed, 128 insertions(+), 66 deletions(-) diff --git a/editorconfig.el b/editorconfig.el index e7bb3c90b1..6cbc76a5d7 100644 --- a/editorconfig.el +++ b/editorconfig.el @@ -5,7 +5,7 @@ ;; Author: EditorConfig Team <editorcon...@googlegroups.com> ;; Version: 0.11.0 ;; URL: https://github.com/editorconfig/editorconfig-emacs#readme -;; Package-Requires: ((emacs "26.1") (nadvice "0.3")) +;; Package-Requires: ((emacs "26.1")) ;; Keywords: convenience editorconfig ;; See @@ -41,9 +41,6 @@ ;;; Code: (require 'cl-lib) -(require 'pcase) - -(require 'nadvice) (eval-when-compile (require 'rx) @@ -73,15 +70,14 @@ coding styles between different editors and IDEs." "Path to EditorConfig executable. Used by `editorconfig--execute-editorconfig-exec'." - :type 'string - :group 'editorconfig) + :type 'string) (define-obsolete-variable-alias 'edconf-get-properties-function 'editorconfig-get-properties-function "0.5") (defcustom editorconfig-get-properties-function - 'editorconfig-core-get-properties-hash + #'editorconfig-core-get-properties-hash "A function which gets EditorConfig properties for specified file. This function will be called with one argument, full path of the target file, @@ -107,13 +103,11 @@ Possible known values are: use `editorconfig-core-get-properties-hash' * `editorconfig-get-properties-from-exec' * Get properties by executing EditorConfig executable" - :type 'function - :group 'editorconfig) + :type 'function) (defcustom editorconfig-mode-lighter " EditorConfig" "Command `editorconfig-mode' lighter string." - :type 'string - :group 'editorconfig) + :type 'string) (define-obsolete-variable-alias 'edconf-custom-hooks @@ -143,8 +137,7 @@ show line numbers on the left: This hook will be run even when there are no matching sections in \".editorconfig\", or no \".editorconfig\" file was found at all." - :type 'hook - :group 'editorconfig) + :type 'hook) (defcustom editorconfig-hack-properties-functions () "A list of function to alter property values before applying them. @@ -166,8 +159,7 @@ overwrite \"indent_style\" property when current `major-mode' is a This hook will be run even when there are no matching sections in \".editorconfig\", or no \".editorconfig\" file was found at all." - :type 'hook - :group 'editorconfig) + :type 'hook) (make-obsolete-variable 'editorconfig-hack-properties-functions "Using `editorconfig-after-apply-functions' instead is recommended, because since 2021/08/30 (v0.9.0) this variable cannot support all properties: @@ -349,21 +341,18 @@ following forms: NOTE: Only the **buffer local** value of VARIABLE will be set." :type '(alist :key-type symbol :value-type sexp) - :risky t - :group 'editorconfig) + :risky t) (defcustom editorconfig-exclude-modes () "Modes in which `editorconfig-mode-apply' will not run." - :type '(repeat (symbol :tag "Major Mode")) - :group 'editorconfig) + :type '(repeat (symbol :tag "Major Mode"))) (defcustom editorconfig-exclude-regexps () "List of regexp for buffer filenames `editorconfig-mode-apply' will not run. When variable `buffer-file-name' matches any of the regexps, then `editorconfig-mode-apply' will not do its work." - :type '(repeat string) - :group 'editorconfig) + :type '(repeat string)) (with-eval-after-load 'recentf (add-to-list 'editorconfig-exclude-regexps (rx-to-string '(seq string-start @@ -375,11 +364,10 @@ When variable `buffer-file-name' matches any of the regexps, then If set, enable that mode when `trim_trailing_whitespace` is set to true. Otherwise, use `delete-trailing-whitespace'." - :type 'symbol - :group 'editorconfig) + :type 'symbol) (defvar editorconfig-properties-hash nil - "Hash object of EditorConfig properties that was enabled for current buffer. + "Hashtable of EditorConfig properties that was enabled for current buffer. Set by `editorconfig-apply' and nil if that is not invoked in current buffer yet.") (make-variable-buffer-local 'editorconfig-properties-hash) @@ -599,21 +587,24 @@ This function will revert buffer when the coding-system has been changed." (revert-buffer-with-coding-system coding-system))) (setq editorconfig--apply-coding-system-currently nil))))) -(defun editorconfig-set-trailing-nl (final-newline) - "Set up requiring final newline by FINAL-NEWLINE. - -This function will set `require-final-newline' and `mode-require-final-newline' -to non-nil when FINAL-NEWLINE is true." - (pcase final-newline - ("true" - ;; keep prefs around how/when the nl is added, if set - otherwise add on save - (setq-local require-final-newline (or require-final-newline t)) - (setq-local mode-require-final-newline (or mode-require-final-newline t))) - ("false" - ;; FIXME: Add functionality for actually REMOVING any trailing newlines here! - ;; (rather than just making sure we don't automagically ADD a new one) - (setq-local require-final-newline nil) - (setq-local mode-require-final-newline nil)))) +;; FIXME: This is from Johan Sundström, who didn't sign the paperwork yet +;; (well, he didn't use `pcase'). Actually, not sure we need to +;; set both `require-final-newline' and `mode-require-final-newline'. +;;(defun editorconfig-set-trailing-nl (final-newline) +;; "Set up requiring final newline by FINAL-NEWLINE. +;; +;;This function will set `require-final-newline' and `mode-require-final-newline' +;;to non-nil when FINAL-NEWLINE is true." +;; (pcase final-newline +;; ("true" +;; ;; keep prefs around how/when the nl is added, if set - otherwise add on save +;; (setq-local require-final-newline (or require-final-newline t)) +;; (setq-local mode-require-final-newline (or mode-require-final-newline t))) +;; ("false" +;; ;; FIXME: Add functionality for actually REMOVING any trailing newlines here! +;; ;; (rather than just making sure we don't automagically ADD a new one) +;; (setq-local require-final-newline nil) +;; (setq-local mode-require-final-newline nil)))) (defun editorconfig-set-trailing-ws (trim-trailing-ws) "Set up trimming of trailing whitespace at end of lines by TRIM-TRAILING-WS." @@ -679,7 +670,7 @@ to non-nil when FINAL-NEWLINE is true." (let ((key-val (split-string prop " *= *"))) (when (> (length key-val) 1) (let ((key (intern (car key-val))) - (val (mapconcat 'identity (cdr key-val) ""))) + (val (mapconcat #'identity (cdr key-val) ""))) (puthash key val properties))))))) (defun editorconfig-get-properties-from-exec (filename) @@ -860,6 +851,58 @@ F is that function, and FILENAME and ARGS are arguments passed to F." ret)) +(defun editorconfig--get-coding-system (_size) + (when (and (stringp auto-coding-file-name) + (file-name-absolute-p auto-coding-file-name) + ;; FIXME: How important is it to support these `disabled-*'? + (not (editorconfig--disabled-for-filename auto-coding-file-name))) + ;; FIXME: Cache the result of `editorconfig-call-get-properties-function'? + (let ((props (editorconfig-call-get-properties-function + auto-coding-file-name))) + (editorconfig-merge-coding-systems (gethash 'end_of_line props) + (gethash 'charset props))))) + +(defvar editorconfig-indent-vars-function + ;; FIXME: Obey `editorconfig-indentation-alist' as best as we can? + ;; Set `smie-indent-basic' if all else fails? + #'ignore) + +(defun editorconfig--get-dir-local-variables () + (when (and (stringp buffer-file-name) + ;; FIXME: How important is it to support these `disabled-*'? + (not (editorconfig--disabled-for-filename buffer-file-name)) + (not (editorconfig--disabled-for-majormode major-mode))) + ;; FIXME: Cache the result of `editorconfig-call-get-properties-function'? + (let ((props (editorconfig-call-get-properties-function filename)) + (alist ())) + (maphash + (lambda (prop setting) + (pcase prop + ('tab_width + (when setting + (push `(tab-width . ,(string-to-number setting)) alist))) + ('indent-size + (let ((size + (cond + ((editorconfig-string-integer-p setting) + (string-to-number setting)) + ((equal size "tab") + (let ((tabsize (gethash 'tab_width props))) + (if tabsize (string-to-number tabsize) + tab-width)))))) + (when size + (setq alist (nconc (funcall editorconfig-indent-vars-function + size) + alist))))) + ;; FIXME!! + )) + props) + ;; FIXME: Actually, we should loop over the "editorconfig-core-handles" + ;; since each one comes from a different directory. + (cons + ¿DIRNAME? ;;FIXME) + alist)))) + ;;;###autoload (define-minor-mode editorconfig-mode "Toggle EditorConfig feature. @@ -868,26 +911,45 @@ To disable EditorConfig in some buffers, modify `editorconfig-exclude-modes' or `editorconfig-exclude-regexps'." :global t :lighter editorconfig-mode-lighter - (let ((modehooks '(prog-mode-hook - text-mode-hook - read-only-mode-hook - ;; Some modes call `kill-all-local-variables' in their init - ;; code, which clears some values set by editorconfig. - ;; For those modes, editorconfig-apply need to be called - ;; explicitly through their hooks. - rpm-spec-mode-hook))) - (if editorconfig-mode - (progn - (advice-add 'find-file-noselect :around 'editorconfig--advice-find-file-noselect) - (advice-add 'insert-file-contents :around 'editorconfig--advice-insert-file-contents) - (dolist (hook modehooks) - (add-hook hook - 'editorconfig-major-mode-hook - t))) - (advice-remove 'find-file-noselect 'editorconfig--advice-find-file-noselect) - (advice-remove 'insert-file-contents 'editorconfig--advice-insert-file-contents) - (dolist (hook modehooks) - (remove-hook hook 'editorconfig-major-mode-hook))))) + (if (boundp 'hack-dir-local-get-variables-functions) ;Emacs≥30 + (if editorconfig-mode + (progn + (add-hook 'hack-dir-local-get-variables-functions + ;; Give it lower precedence than settings from + ;; `dir-locals.el'. + #'editorconfig--get-dir-local-variables t) + ;; `auto-coding-functions' also exists in Emacs<30 but without + ;; access to the file's name via `auto-coding-file-name'. + (add-hook 'auto-coding-functions + #'editorconfig--get-coding-system)) + (remove-hook 'hack-dir-local-get-variables-functions + #'editorconfig--get-dir-local-variables) + (remove-hook 'auto-coding-functions + #'editorconfig--get-coding-system)) + ;; Emacs<30 + (let ((modehooks '(prog-mode-hook + text-mode-hook + read-only-mode-hook + ;; Some modes call `kill-all-local-variables' in their init + ;; code, which clears some values set by editorconfig. + ;; For those modes, editorconfig-apply need to be called + ;; explicitly through their hooks. + ;; FIXME: I don't understand the above comment, because + ;; *all* major modes are supposed to call + ;; `kill-all-local-variables' directory or indirectly. + rpm-spec-mode-hook))) + (if editorconfig-mode + (progn + (advice-add 'find-file-noselect :around #'editorconfig--advice-find-file-noselect) + (advice-add 'insert-file-contents :around #'editorconfig--advice-insert-file-contents) + (dolist (hook modehooks) + (add-hook hook + 'editorconfig-major-mode-hook + t))) + (advice-remove 'find-file-noselect #'editorconfig--advice-find-file-noselect) + (advice-remove 'insert-file-contents #'editorconfig--advice-insert-file-contents) + (dolist (hook modehooks) + (remove-hook hook #'editorconfig-major-mode-hook)))))) ;; (defconst editorconfig--version diff --git a/ert-tests/editorconfig-core-handle.el b/ert-tests/editorconfig-core-handle.el index 2c28137394..454a1ab1be 100644 --- a/ert-tests/editorconfig-core-handle.el +++ b/ert-tests/editorconfig-core-handle.el @@ -23,7 +23,7 @@ ;;; Code: -(require 'editorconfig-core-handle) +(require 'editorconfig-core-handle "../editorconfig-core-handle") (defconst fixtures (concat (file-name-directory load-file-name) "fixtures/") "Path to fixtures.") diff --git a/ert-tests/editorconfig-core.el b/ert-tests/editorconfig-core.el index 126ee1f514..8d7998b4b3 100644 --- a/ert-tests/editorconfig-core.el +++ b/ert-tests/editorconfig-core.el @@ -23,7 +23,7 @@ ;;; Code: -(require 'editorconfig-core) +(require 'editorconfig-core "../editorconfig-core") (set-variable 'vc-handled-backends nil) diff --git a/ert-tests/editorconfig-fnmatch.el b/ert-tests/editorconfig-fnmatch.el index 6f2bd71786..65bb8fb666 100644 --- a/ert-tests/editorconfig-fnmatch.el +++ b/ert-tests/editorconfig-fnmatch.el @@ -23,7 +23,7 @@ ;;; Code: -(require 'editorconfig-fnmatch) +(require 'editorconfig-fnmatch "../editorconfig-fnmatch") (set-variable 'vc-handled-backends nil) @@ -130,13 +130,13 @@ (dolist (args cases-t) (message "-> t: %S" `(editorconfig-fnmatch-p ,@args)) (message " Elapsed: %S" - (car (benchmark-run 3 (should (apply 'editorconfig-fnmatch-p + (car (benchmark-run 3 (should (apply #'editorconfig-fnmatch-p args)))))) (dolist (args cases-nil) (message "-> nil: %S" `(editorconfig-fnmatch-p ,@args)) (message " Elapsed: %S" - (car (benchmark-run 3 (should-not (apply 'editorconfig-fnmatch-p + (car (benchmark-run 3 (should-not (apply #'editorconfig-fnmatch-p args))))))) ) diff --git a/ert-tests/editorconfig.el b/ert-tests/editorconfig.el index 4fcfe13f4e..4dae1fa5eb 100644 --- a/ert-tests/editorconfig.el +++ b/ert-tests/editorconfig.el @@ -23,7 +23,7 @@ ;;; Code: -(require 'editorconfig) +(require 'editorconfig "../editorconfig") (set-variable 'vc-handled-backends nil)