From 977697203f7f063b2f26a9f25bff933e6c939765 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Tue, 3 Nov 2015 02:11:45 +0200 Subject: [PATCH] Rename "search path" to "library roots" * lisp/emacs-lisp/cl-seq.el (cl-set-difference): Retain the order of the elements from CL-LIST1. * test/automated/cl-lib-tests.el (cl-lib-test-set-functions): Update WRT to the above change. * lisp/progmodes/project.el (project-search-path-function): Rename to project-library-roots-function, update the documentation and references. (project-search-path): Likewise, to project-library-roots. (project-roots): Clarify documentation. (project-vc-search-path): Likewise, to project-vc-library-roots. (project-library-roots): In addition to the renames, thread the results through file-name-as-directory. (project-prune-directories): Accept a variable number of arguments. Rename to project-combine-directories. (project-subtract-directories): New function. * lisp/progmodes/elisp-mode.el (elisp--xref-find-references): Append project-roots and project-library-roots together. * lisp/progmodes/etags.el (etags--xref-find-references): Ditto. --- lisp/emacs-lisp/cl-seq.el | 2 +- lisp/progmodes/elisp-mode.el | 13 ++-- lisp/progmodes/etags.el | 7 ++- lisp/progmodes/project.el | 109 ++++++++++++++++++++------------- lisp/progmodes/xref.el | 7 +-- test/automated/cl-lib-tests.el | 8 +-- 6 files changed, 89 insertions(+), 57 deletions(-) diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el index 3aea67ad11..5f0f088121 100644 --- a/lisp/emacs-lisp/cl-seq.el +++ b/lisp/emacs-lisp/cl-seq.el @@ -849,7 +849,7 @@ to avoid corrupting the original LIST1 and LIST2. (memq (car cl-list1) cl-list2)) (push (car cl-list1) cl-res)) (pop cl-list1)) - cl-res)))) + (nreverse cl-res))))) ;;;###autoload (defun cl-nset-difference (cl-list1 cl-list2 &rest cl-keys) diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index bdc304e0aa..298c7a7b53 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -230,7 +230,7 @@ Blank lines separate paragraphs. Semicolons start comments. :group 'lisp (defvar xref-find-function) (defvar xref-identifier-completion-table-function) - (defvar project-search-path-function) + (defvar project-library-roots-function) (lisp-mode-variables nil nil 'elisp) (add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers) (setq-local electric-pair-text-pairs @@ -242,7 +242,7 @@ Blank lines separate paragraphs. Semicolons start comments. (setq-local xref-find-function #'elisp-xref-find) (setq-local xref-identifier-completion-table-function #'elisp--xref-identifier-completion-table) - (setq-local project-search-path-function #'elisp-search-path) + (setq-local project-library-roots-function #'elisp-library-roots) (add-hook 'completion-at-point-functions #'elisp-completion-at-point nil 'local)) @@ -801,7 +801,7 @@ non-nil result supercedes the xrefs produced by xrefs)) -(declare-function project-search-path "project") +(declare-function project-library-roots "project") (declare-function project-current "project") (defun elisp--xref-find-references (symbol) @@ -809,7 +809,10 @@ non-nil result supercedes the xrefs produced by (cl-mapcan (lambda (dir) (xref-collect-references symbol dir)) - (project-search-path (project-current)))) + (let ((pr (project-current))) + (append + (project-roots pr) + (project-library-roots pr))))) (defun elisp--xref-find-apropos (regexp) (apply #'nconc @@ -846,7 +849,7 @@ non-nil result supercedes the xrefs produced by (cl-defmethod xref-location-group ((l xref-elisp-location)) (xref-elisp-location-file l)) -(defun elisp-search-path () +(defun elisp-library-roots () (defvar package-user-dir) (cons package-user-dir load-path)) diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index 0d5fc3a3cd..2d7537a9be 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el @@ -2098,7 +2098,10 @@ for \\[find-tag] (which see)." (cl-mapcan (lambda (dir) (xref-collect-references symbol dir)) - (project-search-path (project-current)))) + (let ((pr (project-current))) + (append + (project-roots pr) + (project-library-roots pr))))) (defun etags--xref-find-definitions (pattern &optional regexp?) ;; This emulates the behaviour of `find-tag-in-order' but instead of @@ -2154,7 +2157,7 @@ for \\[find-tag] (which see)." (with-slots (tag-info) l (nth 1 tag-info))) -(defun etags-search-path () +(defun etags-library-roots () (mapcar #'file-name-directory tags-table-list)) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 186840ae29..ba831204bf 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -23,7 +23,7 @@ ;; projects, and a number of public functions: finding the current ;; root, related project directories, search path, etc. ;; -;; The goal is to make it easy for Lisp programs to operate on the +;; The goal is to make it easier for Lisp programs to operate on the ;; current project, without having to know which package handles ;; detection of that project type, parsing its config files, etc. @@ -38,19 +38,33 @@ Each functions on this hook is called in turn with one argument (the directory) and should return either nil to mean that it is not applicable, or a project instance.") -(declare-function etags-search-path "etags" ()) - -(defvar project-search-path-function #'etags-search-path - "Function that returns a list of source root directories. +;; FIXME: Using the current approach, we don't have access to the +;; "library roots" of language A from buffers of language B, which +;; seems desirable in multi-language projects, at least for some +;; potential uses, like "jump to a file in project or library". +;; +;; We can add a second argument to this function: a file extension, or +;; a language name. Some projects will know the set of languages used +;; in them; for others, like VC-based projects, we'll need +;; auto-detection. I see two options: +;; +;; - That could be implemented as a separate second hook, with a +;; list of functions that return file extensions. +;; +;; - This variable will be turned into a hook with "append" semantics, +;; and each function in it will perform auto-detection when passed +;; nil instead of an actual file extension. Then this hook will, in +;; general, be modified globally, and not from major mode functions. +(defvar project-library-roots-function 'etags-library-roots + "Function that returns a list of library roots. -The directories in which we can recursively look for the -declarations or other references to the symbols used in the -current buffer. Depending on the language, it should include the -headers search path, load path, class path, or so on. +It should return a list of directories that contain source files +related to the current buffer. Depending on the language, it +should include the headers search path, load path, class path, +and so on. -The directory names should be absolute. This variable is -normally set by the major mode. Used in the default -implementation of `project-search-path'.") +The directory names should be absolute. Used in the default +implementation of `project-library-roots'.") ;;;###autoload (defun project-current (&optional dir) @@ -59,35 +73,39 @@ implementation of `project-search-path'.") (run-hook-with-args-until-success 'project-find-functions dir)) ;; FIXME: Add MODE argument, like in `ede-source-paths'? -(cl-defgeneric project-search-path (project) +(cl-defgeneric project-library-roots (project) "Return the list of source root directories. -Any directory roots where source (or header, etc) files used by -the current project may be found, inside or outside of the -current project tree(s). The directory names should be absolute. - -Unless it really knows better, a specialized implementation -should take into account the value returned by -`project-search-path-function' and call -`project-prune-directories' on the result." - (project-prune-directories - (append - ;; We don't know the project layout, like where the sources are, - ;; so we simply include the roots. - (project-roots project) - (funcall project-search-path-function)))) + +It's the list of directories outside of the current project that +contain related source files. + +Project-specific version of `project-library-roots-function', +which see. Unless it knows better, a specialized implementation +should use the value returned by that function." + (project-subtract-directories + (project-combine-directories + (funcall project-library-roots-function)) + (project-roots project))) (cl-defgeneric project-roots (project) - "Return the list of directory roots related to the current project. -It should include the current project root, as well as the roots -of any other currently open projects, if they're meant to be -edited together. The directory names should be absolute.") + "Return the list of directory roots belonging to the current project. + +Most often it's just one directory, which contains the project +file and everything else in the project. But in more advanced +configurations, a project can span multiple directories. + +The rule of tumb for whether to include a directory here, and not +in `project-library-roots', is whether its contents are meant to +be edited together with the rest of the project. + +The directory names should be absolute.") (cl-defgeneric project-ignores (_project _dir) "Return the list of glob patterns to ignore inside DIR. Patterns can match both regular files and directories. To root an entry, start it with `./'. To match directories only, -end it with `/'. DIR must be either one of `project-roots', or -an element of `project-search-path'." +end it with `/'. DIR must be one of `project-roots' or +`project-library-roots'." (require 'grep) (defvar grep-find-ignored-files) (nconc @@ -101,8 +119,8 @@ an element of `project-search-path'." "Project implementation using the VC package." :group 'tools) -(defcustom project-vc-search-path nil - "List ot directories to include in `project-search-path'. +(defcustom project-vc-library-roots nil + "List ot directories to include in `project-library-roots'. The file names can be absolute, or relative to the project root." :type '(repeat file) :safe 'listp) @@ -121,12 +139,12 @@ The file names can be absolute, or relative to the project root." (cl-defmethod project-roots ((project (head vc))) (list (cdr project))) -(cl-defmethod project-search-path ((project (head vc))) +(cl-defmethod project-library-roots ((project (head vc))) (append (let ((root (cdr project))) (mapcar - (lambda (dir) (expand-file-name dir root)) - (project--value-in-dir 'project-vc-search-path root))) + (lambda (dir) (file-name-as-directory (expand-file-name dir root))) + (project--value-in-dir 'project-vc-library-roots root))) (cl-call-next-method))) (cl-defmethod project-ignores ((project (head vc)) dir) @@ -150,13 +168,16 @@ The file names can be absolute, or relative to the project root." (cl-defmethod project-roots ((project (head user))) (list (cdr project))) -(defun project-prune-directories (dirs) - "Returns a copy of DIRS sorted, without subdirectories or non-existing ones." +(defun project-combine-directories (&rest lists-of-dirs) + "Return a sorted and culled list of directory names. +Appends the elements of LISTS-OF-DIRS together, removes +non-existing directories, as well as directories a parent of +whose is already in the list." (let* ((dirs (sort (mapcar (lambda (dir) (file-name-as-directory (expand-file-name dir))) - dirs) + (apply #'append lists-of-dirs)) #'string<)) (ref dirs)) ;; Delete subdirectories from the list. @@ -166,6 +187,12 @@ The file names can be absolute, or relative to the project root." (setq ref (cdr ref)))) (cl-delete-if-not #'file-exists-p dirs))) +(defun project-subtract-directories (files dirs) + "Return a list of elements from FILES that are outside of DIRS. +DIRS must contain directory names." + (cl-set-difference files dirs + :test (lambda (file dir) (string-prefix-p dir file)))) + (defun project--value-in-dir (var dir) (with-temp-buffer (setq default-directory dir) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index a222533936..360d1baf40 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -802,10 +802,9 @@ to search in, and the file name pattern to search for." (dirs (if current-prefix-arg (list (read-directory-name "Base directory: " nil default-directory t)) - (project-prune-directories - (append - (project-roots proj) - (project-search-path proj))))) + (append + (project-roots proj) + (project-library-roots proj)))) (xref-find-function (lambda (_kind regexp) (cl-mapcan diff --git a/test/automated/cl-lib-tests.el b/test/automated/cl-lib-tests.el index 1bdc6d7ca0..d6e6bef2db 100644 --- a/test/automated/cl-lib-tests.el +++ b/test/automated/cl-lib-tests.el @@ -117,8 +117,8 @@ (should (equal (cl-set-difference b b) e)) ;; Note: this test (and others) is sensitive to the order of the ;; result, which is not documented. - (should (equal (cl-set-difference a b) (list c2 "x" "" nil 'a))) - (should (equal (cl-set-difference b a) (list 'x 'y))) + (should (equal (cl-set-difference a b) (list 'a nil "" "x" c2))) + (should (equal (cl-set-difference b a) (list 'y 'x))) ;; We aren't testing whether this is really using `eq' rather than `eql'. (should (equal (cl-set-difference e e :test 'eq) e)) @@ -128,8 +128,8 @@ (should (equal (cl-set-difference b e :test 'eq) b)) (should (equal (cl-set-difference e b :test 'eq) e)) (should (equal (cl-set-difference b b :test 'eq) e)) - (should (equal (cl-set-difference a b :test 'eq) (list c2 "x" "" nil 'a))) - (should (equal (cl-set-difference b a :test 'eq) (list 'x 'y))) + (should (equal (cl-set-difference a b :test 'eq) (list 'a nil "" "x" c2))) + (should (equal (cl-set-difference b a :test 'eq) (list 'y 'x))) (should (equal (cl-union e e) e)) (should (equal (cl-union a e) a)) -- 2.39.2