diff --git a/flycheck-eldev.el b/flycheck-eldev.el index d307835..221f12e 100644 --- a/flycheck-eldev.el +++ b/flycheck-eldev.el @@ -1,6 +1,6 @@ ;;; flycheck-eldev.el --- Eldev support in Flycheck -*- lexical-binding: t -*- -;;; Copyright (C) 2020-2023 Paul Pogonyshev +;;; Copyright (C) 2020-2025 Paul Pogonyshev ;; Author: Paul Pogonyshev ;; Maintainer: Paul Pogonyshev @@ -120,6 +120,19 @@ concerns when checking Eldev (or any Elisp) projects." (const :tag "Don't trust" dont-trust) (const :tag "Trust if ever initialized" trust-if-ever-initialized))) +(defcustom flycheck-eldev-outside-temp-files t + "Normally, Flycheck creates visible files in project directory. +This sometimes leads to various annoying side effects, for example +interference with Git or file watchers on project directory. For this +reason, `flycheck-eldev' puts such files in `temporary-file-directory' +by default (utilizing built-in Flycheck capability, that is simply not +used for Elisp). + +However, if this causes any issues, you can revert to the standard +Flycheck behavior by setting this to nil." + :group 'flycheck-eldev + :type 'boolean) + (defvar flycheck-eldev-active t "Whether Eldev extension to Flycheck is active.") @@ -207,8 +220,15 @@ If FROM is nil, search from `default-directory'." ;; rewrite the command line provided by the standard checker, so we get any ;; future improvements for free. (let* ((super (let ((flycheck-emacs-lisp-load-path nil) - (flycheck-emacs-lisp-initialize-packages nil)) - (flycheck-checker-substituted-arguments 'emacs-lisp))) + (flycheck-emacs-lisp-initialize-packages nil) + (original-command-template (flycheck-checker-get 'emacs-lisp 'command))) + (unwind-protect + (progn + (when flycheck-eldev-outside-temp-files + (setf (flycheck-checker-get 'emacs-lisp 'command) + (mapcar (lambda (x) (if (eq x 'source-inplace) 'source x)) original-command-template))) + (flycheck-checker-substituted-arguments 'emacs-lisp)) + (setf (flycheck-checker-get 'emacs-lisp 'command) original-command-template)))) (head (-drop-last 2 super)) (tail (-take-last 2 super)) (filename (cadr tail)) @@ -237,20 +257,16 @@ If FROM is nil, search from `default-directory'." ;; `eldev-project-main-file' is specified, this does nothing. "--setup-first" ,(flycheck-sexp-to-string - `(advice-add #'eldev--package-dir-info :around + `(advice-add #'eldev-package-descriptor :around (lambda (original) (eldev-advised (#'insert-file-contents :around (lambda (original filename &rest arguments) - (unless (file-equal-p filename ,real-filename) + (if (file-equal-p filename ,real-filename) + ;; Load the temp file instead. + (apply original ,filename arguments) (apply original filename arguments)))) (funcall original))))) - ;; When checking project's main file, use the temporary as the main file - ;; instead. - "--setup" - ,(flycheck-sexp-to-string - `(when (and eldev-project-main-file (file-equal-p eldev-project-main-file ,real-filename)) - (setf eldev-project-main-file ,filename))) ;; Special handling for test files: load extra dependencies as if testing ;; now. Likewise for loading roots. "--setup" diff --git a/test/flycheck-eldev-test.el b/test/flycheck-eldev-test.el index 6c31b6e..3110e89 100644 --- a/test/flycheck-eldev-test.el +++ b/test/flycheck-eldev-test.el @@ -38,21 +38,26 @@ (:unknown-projects (setf flycheck-eldev-unknown-projects (pop body))) (:enable-checkdoc (if (pop body) (push 'emacs-lisp-checkdoc enabled-checkers) + ;; We don't use `emacs-lisp-checkdoc' unless explicitly enabled. (setf enabled-checkers (delq 'emacs-lisp-checkdoc enabled-checkers)))))) - ;; Don't use `emacs-lisp-checkdoc'. - `(let ((flycheck-checkers (--filter (memq it ',enabled-checkers) flycheck-checkers)) - (flycheck-disabled-checkers nil) - (flycheck-check-syntax-automatically nil) - (flycheck-eldev-whitelist ',flycheck-eldev-whitelist) - (flycheck-eldev-blacklist ',flycheck-eldev-blacklist) - (flycheck-eldev-unknown-projects ',flycheck-eldev-unknown-projects) - (file (expand-file-name ,file flycheck-eldev--test-dir))) - (with-temp-buffer - (insert-file-contents file t) - (setf default-directory (file-name-directory file)) - (emacs-lisp-mode) - (flycheck-mode 1) - ,@body)))) + ;; Currently not testing if `flycheck-eldev-outside-temp-files' actually achieves what it promises, but at + ;; least make sure that everything works regardless if that is set to t or nil. + `(dolist (outside-temp-files '(nil t)) + (ert-info ((format "flycheck-eldev-outside-temp-files = %s" outside-temp-files)) + (let ((flycheck-checkers (--filter (memq it ',enabled-checkers) flycheck-checkers)) + (flycheck-disabled-checkers nil) + (flycheck-check-syntax-automatically nil) + (flycheck-eldev-whitelist ',flycheck-eldev-whitelist) + (flycheck-eldev-blacklist ',flycheck-eldev-blacklist) + (flycheck-eldev-unknown-projects ',flycheck-eldev-unknown-projects) + (flycheck-eldev-outside-temp-files outside-temp-files) + (file (expand-file-name ,file flycheck-eldev--test-dir))) + (with-temp-buffer + (insert-file-contents file t) + (setf default-directory (file-name-directory file)) + (emacs-lisp-mode) + (flycheck-mode 1) + ,@body)))))) (defmacro flycheck-eldev--test-with-temp-file (file content-creation &rest body) (declare (indent 2) (debug (sexp form body))) @@ -96,13 +101,13 @@ (flycheck-eldev-ert-defargtest flycheck-eldev-basics-1 (file) - ("project-a/project-a.el" "project-b/project-b.el" "project-b/project-b-util.el") + ("project-a/project-a.el" "project-b/project-b.el" "project-b/project-b-util.el" "project-c/project-c.el" "project-c/project-c-util.el") (flycheck-eldev--test file (flycheck-eldev--test-recheck) (flycheck-eldev--test-expect-no-errors))) (flycheck-eldev-ert-defargtest flycheck-eldev-basics-2 (file) - ("project-a/Eldev" "project-b/Eldev") + ("project-a/Eldev" "project-b/Eldev" "project-c/Eldev") (flycheck-eldev--test file ;; Enable `checkdoc'. Must be disabled at runtime (currently through our own hack) ;; for the test to pass. @@ -132,7 +137,9 @@ (flycheck-eldev--test-expect-no-errors))) (flycheck-eldev-ert-defargtest flycheck-eldev-test-file-1 (file) - ("project-a/test/project-a.el" "project-b/test/project-b.el" "project-b/test/project-b-util.el") + ("project-a/test/project-a.el" + "project-b/test/project-b.el" "project-b/test/project-b-util.el" + "project-c/test/project-c.el" "project-c/test/project-c-util.el") (flycheck-eldev--test file (flycheck-eldev--test-recheck) (flycheck-eldev--test-expect-no-errors))) @@ -144,12 +151,15 @@ (flycheck-eldev--test-expect-errors '(:matches "dependency-a"))))) ;; Test that removing dependencies gives errors immediately. -(ert-deftest flycheck-eldev-remove-dependency-1 () - (flycheck-eldev--test "project-a/project-a.el" - (search-forward "(dependency-a \"1.0\")") +(flycheck-eldev-ert-defargtest flycheck-eldev-remove-dependency-1 (file dependency) + (("project-a/project-a.el" 'dependency-a) + ("project-b/project-b.el" 'dependency-b) + ("project-c/project-c.el" 'dependency-b)) + (flycheck-eldev--test file + (re-search-forward (format "(%s \"[^\"]+\")" dependency)) (replace-match "") (flycheck-eldev--test-recheck) - (flycheck-eldev--test-expect-errors '(:matches "dependency-a")))) + (flycheck-eldev--test-expect-errors `(:matches ,(symbol-name dependency))))) ;; Test that adding unaccessible dependencies gives errors immediately. (ert-deftest flycheck-eldev-add-dependency-1 () diff --git a/test/project-c/Eldev b/test/project-c/Eldev new file mode 100644 index 0000000..1608135 --- /dev/null +++ b/test/project-c/Eldev @@ -0,0 +1,7 @@ +; -*- mode: emacs-lisp; lexical-binding: t -*- + +(eldev-use-package-archive `("archive-a" . ,(expand-file-name "../package-archive-a"))) + +;; The point of this test project is to verify that explicitly set `eldev-project-main-file' still works as +;; expected. +(setf eldev-project-main-file "project-c.el") diff --git a/test/project-c/project-c-util.el b/test/project-c/project-c-util.el new file mode 100644 index 0000000..1d456e2 --- /dev/null +++ b/test/project-c/project-c-util.el @@ -0,0 +1,9 @@ +;;; -*- lexical-binding: t; -*- + +(require 'dependency-a) +(require 'dependency-b) + +(defun project-c-do-hello () + (dependency-a-hello)) + +(provide 'project-c-util) diff --git a/test/project-c/project-c.el b/test/project-c/project-c.el new file mode 100644 index 0000000..588682a --- /dev/null +++ b/test/project-c/project-c.el @@ -0,0 +1,24 @@ +;;; project-c.el --- Exactly like `project-b', but with explicitly set `eldev-project-main-file' -*- lexical-binding: t; -*- + +;; Version: 1.0 +;; Homepage: https://example.com/ +;; Package-Requires: ((dependency-a "1.0") (dependency-b "1.0")) + +;;; Commentary: + +;; Comments to make linters happy. + +;;; Code: + +(require 'project-c-util) +(require 'dependency-a) +(require 'dependency-b) + +(defun project-c-hello () + "Invoke `project-c-do-hello' from `project-c-util'. +This docstring exists to make linters happy." + (project-c-do-hello)) + +(provide 'project-c) + +;;; project-c.el ends here diff --git a/test/project-c/test/project-c-util.el b/test/project-c/test/project-c-util.el new file mode 100644 index 0000000..515b06b --- /dev/null +++ b/test/project-c/test/project-c-util.el @@ -0,0 +1,6 @@ +;; -*- lexical-binding: t; -*- + +(require 'project-c) +(require 'ert) + +(provide 'test/project-c-util) diff --git a/test/project-c/test/project-c.el b/test/project-c/test/project-c.el new file mode 100644 index 0000000..a7284e9 --- /dev/null +++ b/test/project-c/test/project-c.el @@ -0,0 +1,6 @@ +;; -*- lexical-binding: t; -*- + +(require 'test/project-c-util) + +(ert-deftest project-c-test-hello () + (should (string= (project-c-hello) "Hello")))