diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index b5c14cfa171..c0262d2894d 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -2322,7 +2322,9 @@ Using this variable you can add more ignore patterns to the project, to exclude more files from the project's file listing. The value is a list of glob strings. They can match both regular files and directories. To anchor an entry to the project root, start it with @code{./}. To match -directories only, end it with @code{/}. +directories only, end it with @code{/}. When this variable has +directory-local value, it will only be applied to the corresponding +directory subtree. @end defopt @defopt project-vc-name diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index c0816620aab..9467e5d1356 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -466,7 +466,10 @@ the buffer's value of `default-directory'." :group 'project) (defcustom project-vc-ignores nil - "List of patterns to add to `project-ignores'." + "List of patterns to add to `project-ignores'. + +Only global or directory-local values are supported. And directory-local +values will be applied to the corresponding directory subtrees." :type '(repeat string)) ;; Change to `list-of-strings-p' when support for Emacs 28 is dropped. ;;;###autoload(put 'project-vc-ignores 'safe-local-variable (lambda (val) (and (listp val) (not (memq nil (mapcar #'stringp val)))))) @@ -688,12 +691,14 @@ See `project-vc-extra-root-markers' for the marker value format.") (cl-defmethod project-files ((project (head vc)) &optional dirs) (mapcan (lambda (dir) - (let ((ignores (project--value-in-dir 'project-vc-ignores (nth 2 project))) + (let ((ignores (project--value-in-dir 'project-vc-ignores dir)) (backend (project-vc--backend project dir))) (if backend (vc-call-backend backend 'project-list-files dir ignores) (project--files-in-directory - dir (append ignores (project-ignores nil nil)))))) + dir (append ignores (append + (project-ignores nil nil) + ignores)))))) (or dirs (list (project-root project))))) @@ -830,9 +835,9 @@ See `project-vc-extra-root-markers' for the marker value format.") (file-missing nil))) (cl-defmethod project-ignores ((project (head vc)) dir) - (let ((root (nth 2 project))) - (project--vc-ignores dir (project-vc--backend project dir) - (project--value-in-dir 'project-vc-ignores root)))) + (project--vc-ignores dir + (project-vc--backend project dir) + (project--value-in-dir 'project-vc-ignores dir))) (defun project--vc-ignores (dir backend extra-ignores) (append @@ -890,7 +895,7 @@ DIRS must contain directory names." (defun project--value-in-dir (var dir) (with-temp-buffer - (setq default-directory dir) + (setq default-directory (file-name-as-directory dir)) (let ((enable-local-variables :all)) (hack-dir-local-variables)) ;; Don't use `hack-local-variables-apply' to avoid setting modes. diff --git a/test/lisp/progmodes/project-tests.el b/test/lisp/progmodes/project-tests.el index e353eef728e..09045114dc2 100644 --- a/test/lisp/progmodes/project-tests.el +++ b/test/lisp/progmodes/project-tests.el @@ -162,7 +162,7 @@ When `project-ignores' includes a name matching project dir." (mapcar #'file-name-nondirectory (project-files project)))))) (ert-deftest project-vc-supports-files-in-subdirectory () - "Check that it lists only files from subdirectory." + "Check that it lists only files from a repo's subdirectory." (skip-unless (eq (vc-responsible-backend default-directory) 'Git)) (let* ((dir (ert-resource-directory)) (_ (vc-file-clearprops dir)) @@ -170,7 +170,27 @@ When `project-ignores' includes a name matching project dir." (project (project-current nil dir))) (should-not (null project)) (should (string-match-p "/test/lisp/progmodes/\\'" (project-root project))) - (should (equal '(".dir-locals.el" "etc" "foo") + (should (equal `(,@(when (version<= "2.13" (vc-git--program-version)) + (list ".dir-locals.el")) + "foo") + (mapcar #'file-name-nondirectory + (project-files project + (list dir))))))) + +(ert-deftest project-vc-ignores-in-external-directory () + "Check that it applies project-vc-ignores when DIR is external to root." + (skip-unless (eq (vc-responsible-backend default-directory) 'Git)) + (let* ((dir (ert-resource-directory)) + (_ (vc-file-clearprops dir)) + ;; Do not detect VC backend. + (project-vc-backend-markers-alist nil) + (project-vc-extra-root-markers '("configure.ac")) + (project (project-current nil (expand-file-name "../autoconf-resources/" dir)))) + (should-not (null project)) + (should (string-match-p "/test/lisp/progmodes/autoconf-resources/\\'" (project-root project))) + (should (equal `(,@(when (version<= "2.13" (vc-git--program-version)) + (list ".dir-locals.el")) + "foo") (mapcar #'file-name-nondirectory (project-files project (list dir)))))))