diff options
| author | Dmitry Gutov | 2015-11-10 02:47:46 +0200 |
|---|---|---|
| committer | Dmitry Gutov | 2015-11-10 02:47:46 +0200 |
| commit | 0be6fb8e17f708fe03430d0b1e701810ae51b5e3 (patch) | |
| tree | 03abad9d6697b70fbc6f26e5504c7c20ae070f3d | |
| parent | 3c3aad733522365a8fe729d7c92e64e98bc4ce92 (diff) | |
| parent | 1c72afb7aa48c2ea06103113ef70ccea0c1c961d (diff) | |
| download | emacs-0be6fb8e17f708fe03430d0b1e701810ae51b5e3.tar.gz emacs-0be6fb8e17f708fe03430d0b1e701810ae51b5e3.zip | |
Merge branch 'project-next'
| -rw-r--r-- | lisp/emacs-lisp/cl-seq.el | 2 | ||||
| -rw-r--r-- | lisp/progmodes/elisp-mode.el | 13 | ||||
| -rw-r--r-- | lisp/progmodes/etags.el | 7 | ||||
| -rw-r--r-- | lisp/progmodes/project.el | 197 | ||||
| -rw-r--r-- | lisp/progmodes/xref.el | 108 | ||||
| -rw-r--r-- | lisp/vc/vc.el | 5 | ||||
| -rw-r--r-- | test/automated/cl-lib-tests.el | 8 |
7 files changed, 186 insertions, 154 deletions
diff --git a/lisp/emacs-lisp/cl-seq.el b/lisp/emacs-lisp/cl-seq.el index 3aea67ad11b..5f0f0881210 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. | |||
| 849 | (memq (car cl-list1) cl-list2)) | 849 | (memq (car cl-list1) cl-list2)) |
| 850 | (push (car cl-list1) cl-res)) | 850 | (push (car cl-list1) cl-res)) |
| 851 | (pop cl-list1)) | 851 | (pop cl-list1)) |
| 852 | cl-res)))) | 852 | (nreverse cl-res))))) |
| 853 | 853 | ||
| 854 | ;;;###autoload | 854 | ;;;###autoload |
| 855 | (defun cl-nset-difference (cl-list1 cl-list2 &rest cl-keys) | 855 | (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 8ea17b74ddb..a19542fb204 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el | |||
| @@ -230,7 +230,7 @@ Blank lines separate paragraphs. Semicolons start comments. | |||
| 230 | :group 'lisp | 230 | :group 'lisp |
| 231 | (defvar xref-find-function) | 231 | (defvar xref-find-function) |
| 232 | (defvar xref-identifier-completion-table-function) | 232 | (defvar xref-identifier-completion-table-function) |
| 233 | (defvar project-search-path-function) | 233 | (defvar project-library-roots-function) |
| 234 | (lisp-mode-variables nil nil 'elisp) | 234 | (lisp-mode-variables nil nil 'elisp) |
| 235 | (add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers) | 235 | (add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers) |
| 236 | (setq-local electric-pair-text-pairs | 236 | (setq-local electric-pair-text-pairs |
| @@ -242,7 +242,7 @@ Blank lines separate paragraphs. Semicolons start comments. | |||
| 242 | (setq-local xref-find-function #'elisp-xref-find) | 242 | (setq-local xref-find-function #'elisp-xref-find) |
| 243 | (setq-local xref-identifier-completion-table-function | 243 | (setq-local xref-identifier-completion-table-function |
| 244 | #'elisp--xref-identifier-completion-table) | 244 | #'elisp--xref-identifier-completion-table) |
| 245 | (setq-local project-search-path-function #'elisp-search-path) | 245 | (setq-local project-library-roots-function #'elisp-library-roots) |
| 246 | (add-hook 'completion-at-point-functions | 246 | (add-hook 'completion-at-point-functions |
| 247 | #'elisp-completion-at-point nil 'local)) | 247 | #'elisp-completion-at-point nil 'local)) |
| 248 | 248 | ||
| @@ -801,7 +801,7 @@ non-nil result supercedes the xrefs produced by | |||
| 801 | 801 | ||
| 802 | xrefs)) | 802 | xrefs)) |
| 803 | 803 | ||
| 804 | (declare-function project-search-path "project") | 804 | (declare-function project-library-roots "project") |
| 805 | (declare-function project-current "project") | 805 | (declare-function project-current "project") |
| 806 | 806 | ||
| 807 | (defun elisp--xref-find-references (symbol) | 807 | (defun elisp--xref-find-references (symbol) |
| @@ -809,7 +809,10 @@ non-nil result supercedes the xrefs produced by | |||
| 809 | (cl-mapcan | 809 | (cl-mapcan |
| 810 | (lambda (dir) | 810 | (lambda (dir) |
| 811 | (xref-collect-references symbol dir)) | 811 | (xref-collect-references symbol dir)) |
| 812 | (project-search-path (project-current)))) | 812 | (let ((pr (project-current t))) |
| 813 | (append | ||
| 814 | (project-roots pr) | ||
| 815 | (project-library-roots pr))))) | ||
| 813 | 816 | ||
| 814 | (defun elisp--xref-find-apropos (regexp) | 817 | (defun elisp--xref-find-apropos (regexp) |
| 815 | (apply #'nconc | 818 | (apply #'nconc |
| @@ -846,7 +849,7 @@ non-nil result supercedes the xrefs produced by | |||
| 846 | (cl-defmethod xref-location-group ((l xref-elisp-location)) | 849 | (cl-defmethod xref-location-group ((l xref-elisp-location)) |
| 847 | (xref-elisp-location-file l)) | 850 | (xref-elisp-location-file l)) |
| 848 | 851 | ||
| 849 | (defun elisp-search-path () | 852 | (defun elisp-library-roots () |
| 850 | (defvar package-user-dir) | 853 | (defvar package-user-dir) |
| 851 | (cons package-user-dir load-path)) | 854 | (cons package-user-dir load-path)) |
| 852 | 855 | ||
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index 0d5fc3a3cd3..38c5cc2bdb6 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el | |||
| @@ -2098,7 +2098,10 @@ for \\[find-tag] (which see)." | |||
| 2098 | (cl-mapcan | 2098 | (cl-mapcan |
| 2099 | (lambda (dir) | 2099 | (lambda (dir) |
| 2100 | (xref-collect-references symbol dir)) | 2100 | (xref-collect-references symbol dir)) |
| 2101 | (project-search-path (project-current)))) | 2101 | (let ((pr (project-current t))) |
| 2102 | (append | ||
| 2103 | (project-roots pr) | ||
| 2104 | (project-library-roots pr))))) | ||
| 2102 | 2105 | ||
| 2103 | (defun etags--xref-find-definitions (pattern &optional regexp?) | 2106 | (defun etags--xref-find-definitions (pattern &optional regexp?) |
| 2104 | ;; This emulates the behaviour of `find-tag-in-order' but instead of | 2107 | ;; This emulates the behaviour of `find-tag-in-order' but instead of |
| @@ -2154,7 +2157,7 @@ for \\[find-tag] (which see)." | |||
| 2154 | (with-slots (tag-info) l | 2157 | (with-slots (tag-info) l |
| 2155 | (nth 1 tag-info))) | 2158 | (nth 1 tag-info))) |
| 2156 | 2159 | ||
| 2157 | (defun etags-search-path () | 2160 | (defun etags-library-roots () |
| 2158 | (mapcar #'file-name-directory tags-table-list)) | 2161 | (mapcar #'file-name-directory tags-table-list)) |
| 2159 | 2162 | ||
| 2160 | 2163 | ||
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 186840ae29b..0da6084a1e3 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | ;; projects, and a number of public functions: finding the current | 23 | ;; projects, and a number of public functions: finding the current |
| 24 | ;; root, related project directories, search path, etc. | 24 | ;; root, related project directories, search path, etc. |
| 25 | ;; | 25 | ;; |
| 26 | ;; The goal is to make it easy for Lisp programs to operate on the | 26 | ;; The goal is to make it easier for Lisp programs to operate on the |
| 27 | ;; current project, without having to know which package handles | 27 | ;; current project, without having to know which package handles |
| 28 | ;; detection of that project type, parsing its config files, etc. | 28 | ;; detection of that project type, parsing its config files, etc. |
| 29 | 29 | ||
| @@ -31,63 +31,93 @@ | |||
| 31 | 31 | ||
| 32 | (require 'cl-generic) | 32 | (require 'cl-generic) |
| 33 | 33 | ||
| 34 | (defvar project-find-functions (list #'project-try-vc | 34 | (defvar project-find-functions (list #'project-try-vc) |
| 35 | #'project-ask-user) | ||
| 36 | "Special hook to find the project containing a given directory. | 35 | "Special hook to find the project containing a given directory. |
| 37 | Each functions on this hook is called in turn with one | 36 | Each functions on this hook is called in turn with one |
| 38 | argument (the directory) and should return either nil to mean | 37 | argument (the directory) and should return either nil to mean |
| 39 | that it is not applicable, or a project instance.") | 38 | that it is not applicable, or a project instance.") |
| 40 | 39 | ||
| 41 | (declare-function etags-search-path "etags" ()) | 40 | ;; FIXME: Using the current approach, we don't have access to the |
| 42 | 41 | ;; "library roots" of language A from buffers of language B, which | |
| 43 | (defvar project-search-path-function #'etags-search-path | 42 | ;; seems desirable in multi-language projects, at least for some |
| 44 | "Function that returns a list of source root directories. | 43 | ;; potential uses, like "jump to a file in project or library". |
| 44 | ;; | ||
| 45 | ;; We can add a second argument to this function: a file extension, or | ||
| 46 | ;; a language name. Some projects will know the set of languages used | ||
| 47 | ;; in them; for others, like VC-based projects, we'll need | ||
| 48 | ;; auto-detection. I see two options: | ||
| 49 | ;; | ||
| 50 | ;; - That could be implemented as a separate second hook, with a | ||
| 51 | ;; list of functions that return file extensions. | ||
| 52 | ;; | ||
| 53 | ;; - This variable will be turned into a hook with "append" semantics, | ||
| 54 | ;; and each function in it will perform auto-detection when passed | ||
| 55 | ;; nil instead of an actual file extension. Then this hook will, in | ||
| 56 | ;; general, be modified globally, and not from major mode functions. | ||
| 57 | (defvar project-library-roots-function 'etags-library-roots | ||
| 58 | "Function that returns a list of library roots. | ||
| 45 | 59 | ||
| 46 | The directories in which we can recursively look for the | 60 | It should return a list of directories that contain source files |
| 47 | declarations or other references to the symbols used in the | 61 | related to the current buffer. Depending on the language, it |
| 48 | current buffer. Depending on the language, it should include the | 62 | should include the headers search path, load path, class path, |
| 49 | headers search path, load path, class path, or so on. | 63 | and so on. |
| 50 | 64 | ||
| 51 | The directory names should be absolute. This variable is | 65 | The directory names should be absolute. Used in the default |
| 52 | normally set by the major mode. Used in the default | 66 | implementation of `project-library-roots'.") |
| 53 | implementation of `project-search-path'.") | ||
| 54 | 67 | ||
| 55 | ;;;###autoload | 68 | ;;;###autoload |
| 56 | (defun project-current (&optional dir) | 69 | (defun project-current (&optional maybe-prompt dir) |
| 57 | "Return the project instance in DIR or `default-directory'." | 70 | "Return the project instance in DIR or `default-directory'. |
| 71 | When no project found in DIR, and MAYBE-PROMPT is non-nil, ask | ||
| 72 | the user for a different directory to look in." | ||
| 58 | (unless dir (setq dir default-directory)) | 73 | (unless dir (setq dir default-directory)) |
| 74 | (let ((pr (project--find-in-directory dir))) | ||
| 75 | (cond | ||
| 76 | (pr) | ||
| 77 | (maybe-prompt | ||
| 78 | (setq dir (read-directory-name "Choose the project directory: " dir nil t) | ||
| 79 | pr (project--find-in-directory dir)) | ||
| 80 | (unless pr | ||
| 81 | (user-error "No project found in `%s'" dir)))) | ||
| 82 | pr)) | ||
| 83 | |||
| 84 | (defun project--find-in-directory (dir) | ||
| 59 | (run-hook-with-args-until-success 'project-find-functions dir)) | 85 | (run-hook-with-args-until-success 'project-find-functions dir)) |
| 60 | 86 | ||
| 61 | ;; FIXME: Add MODE argument, like in `ede-source-paths'? | 87 | ;; FIXME: Add MODE argument, like in `ede-source-paths'? |
| 62 | (cl-defgeneric project-search-path (project) | 88 | (cl-defgeneric project-library-roots (project) |
| 63 | "Return the list of source root directories. | 89 | "Return the list of library roots for PROJECT. |
| 64 | Any directory roots where source (or header, etc) files used by | 90 | |
| 65 | the current project may be found, inside or outside of the | 91 | It's the list of directories outside of the project that contain |
| 66 | current project tree(s). The directory names should be absolute. | 92 | related source files. |
| 67 | 93 | ||
| 68 | Unless it really knows better, a specialized implementation | 94 | Project-specific version of `project-library-roots-function', |
| 69 | should take into account the value returned by | 95 | which see. Unless it knows better, a specialized implementation |
| 70 | `project-search-path-function' and call | 96 | should use the value returned by that function." |
| 71 | `project-prune-directories' on the result." | 97 | (project-subtract-directories |
| 72 | (project-prune-directories | 98 | (project-combine-directories |
| 73 | (append | 99 | (funcall project-library-roots-function)) |
| 74 | ;; We don't know the project layout, like where the sources are, | 100 | (project-roots project))) |
| 75 | ;; so we simply include the roots. | ||
| 76 | (project-roots project) | ||
| 77 | (funcall project-search-path-function)))) | ||
| 78 | 101 | ||
| 79 | (cl-defgeneric project-roots (project) | 102 | (cl-defgeneric project-roots (project) |
| 80 | "Return the list of directory roots related to the current project. | 103 | "Return the list of directory roots belonging to the current project. |
| 81 | It should include the current project root, as well as the roots | 104 | |
| 82 | of any other currently open projects, if they're meant to be | 105 | Most often it's just one directory, which contains the project |
| 83 | edited together. The directory names should be absolute.") | 106 | file and everything else in the project. But in more advanced |
| 107 | configurations, a project can span multiple directories. | ||
| 108 | |||
| 109 | The rule of tumb for whether to include a directory here, and not | ||
| 110 | in `project-library-roots', is whether its contents are meant to | ||
| 111 | be edited together with the rest of the project. | ||
| 112 | |||
| 113 | The directory names should be absolute.") | ||
| 84 | 114 | ||
| 85 | (cl-defgeneric project-ignores (_project _dir) | 115 | (cl-defgeneric project-ignores (_project _dir) |
| 86 | "Return the list of glob patterns to ignore inside DIR. | 116 | "Return the list of glob patterns to ignore inside DIR. |
| 87 | Patterns can match both regular files and directories. | 117 | Patterns can match both regular files and directories. |
| 88 | To root an entry, start it with `./'. To match directories only, | 118 | To root an entry, start it with `./'. To match directories only, |
| 89 | end it with `/'. DIR must be either one of `project-roots', or | 119 | end it with `/'. DIR must be one of `project-roots' or |
| 90 | an element of `project-search-path'." | 120 | `project-library-roots'." |
| 91 | (require 'grep) | 121 | (require 'grep) |
| 92 | (defvar grep-find-ignored-files) | 122 | (defvar grep-find-ignored-files) |
| 93 | (nconc | 123 | (nconc |
| @@ -101,8 +131,8 @@ an element of `project-search-path'." | |||
| 101 | "Project implementation using the VC package." | 131 | "Project implementation using the VC package." |
| 102 | :group 'tools) | 132 | :group 'tools) |
| 103 | 133 | ||
| 104 | (defcustom project-vc-search-path nil | 134 | (defcustom project-vc-library-roots nil |
| 105 | "List ot directories to include in `project-search-path'. | 135 | "List ot directories to include in `project-library-roots'. |
| 106 | The file names can be absolute, or relative to the project root." | 136 | The file names can be absolute, or relative to the project root." |
| 107 | :type '(repeat file) | 137 | :type '(repeat file) |
| 108 | :safe 'listp) | 138 | :safe 'listp) |
| @@ -121,13 +151,16 @@ The file names can be absolute, or relative to the project root." | |||
| 121 | (cl-defmethod project-roots ((project (head vc))) | 151 | (cl-defmethod project-roots ((project (head vc))) |
| 122 | (list (cdr project))) | 152 | (list (cdr project))) |
| 123 | 153 | ||
| 124 | (cl-defmethod project-search-path ((project (head vc))) | 154 | (cl-defmethod project-library-roots ((project (head vc))) |
| 125 | (append | 155 | (project-subtract-directories |
| 126 | (let ((root (cdr project))) | 156 | (project-combine-directories |
| 127 | (mapcar | 157 | (append |
| 128 | (lambda (dir) (expand-file-name dir root)) | 158 | (let ((root (cdr project))) |
| 129 | (project--value-in-dir 'project-vc-search-path root))) | 159 | (mapcar |
| 130 | (cl-call-next-method))) | 160 | (lambda (dir) (file-name-as-directory (expand-file-name dir root))) |
| 161 | (project--value-in-dir 'project-vc-library-roots root))) | ||
| 162 | (funcall project-library-roots-function))) | ||
| 163 | (project-roots project))) | ||
| 131 | 164 | ||
| 132 | (cl-defmethod project-ignores ((project (head vc)) dir) | 165 | (cl-defmethod project-ignores ((project (head vc)) dir) |
| 133 | (let* ((root (cdr project)) | 166 | (let* ((root (cdr project)) |
| @@ -144,19 +177,16 @@ The file names can be absolute, or relative to the project root." | |||
| 144 | (project--value-in-dir 'project-vc-ignores root) | 177 | (project--value-in-dir 'project-vc-ignores root) |
| 145 | (cl-call-next-method)))) | 178 | (cl-call-next-method)))) |
| 146 | 179 | ||
| 147 | (defun project-ask-user (dir) | 180 | (defun project-combine-directories (&rest lists-of-dirs) |
| 148 | (cons 'user (read-directory-name "Project root: " dir nil t))) | 181 | "Return a sorted and culled list of directory names. |
| 149 | 182 | Appends the elements of LISTS-OF-DIRS together, removes | |
| 150 | (cl-defmethod project-roots ((project (head user))) | 183 | non-existing directories, as well as directories a parent of |
| 151 | (list (cdr project))) | 184 | whose is already in the list." |
| 152 | |||
| 153 | (defun project-prune-directories (dirs) | ||
| 154 | "Returns a copy of DIRS sorted, without subdirectories or non-existing ones." | ||
| 155 | (let* ((dirs (sort | 185 | (let* ((dirs (sort |
| 156 | (mapcar | 186 | (mapcar |
| 157 | (lambda (dir) | 187 | (lambda (dir) |
| 158 | (file-name-as-directory (expand-file-name dir))) | 188 | (file-name-as-directory (expand-file-name dir))) |
| 159 | dirs) | 189 | (apply #'append lists-of-dirs)) |
| 160 | #'string<)) | 190 | #'string<)) |
| 161 | (ref dirs)) | 191 | (ref dirs)) |
| 162 | ;; Delete subdirectories from the list. | 192 | ;; Delete subdirectories from the list. |
| @@ -166,11 +196,66 @@ The file names can be absolute, or relative to the project root." | |||
| 166 | (setq ref (cdr ref)))) | 196 | (setq ref (cdr ref)))) |
| 167 | (cl-delete-if-not #'file-exists-p dirs))) | 197 | (cl-delete-if-not #'file-exists-p dirs))) |
| 168 | 198 | ||
| 199 | (defun project-subtract-directories (files dirs) | ||
| 200 | "Return a list of elements from FILES that are outside of DIRS. | ||
| 201 | DIRS must contain directory names." | ||
| 202 | ;; Sidestep the issue of expanded/abbreviated file names here. | ||
| 203 | (cl-set-difference files dirs :test #'file-in-directory-p)) | ||
| 204 | |||
| 169 | (defun project--value-in-dir (var dir) | 205 | (defun project--value-in-dir (var dir) |
| 170 | (with-temp-buffer | 206 | (with-temp-buffer |
| 171 | (setq default-directory dir) | 207 | (setq default-directory dir) |
| 172 | (hack-dir-local-variables-non-file-buffer) | 208 | (hack-dir-local-variables-non-file-buffer) |
| 173 | (symbol-value var))) | 209 | (symbol-value var))) |
| 174 | 210 | ||
| 211 | (declare-function grep-read-files "grep") | ||
| 212 | (declare-function xref-collect-matches "xref") | ||
| 213 | (declare-function xref--show-xrefs "xref") | ||
| 214 | |||
| 215 | ;;;###autoload | ||
| 216 | (defun project-find-regexp (regexp) | ||
| 217 | "Find all matches for REGEXP in the current project. | ||
| 218 | With \\[universal-argument] prefix, you can specify the directory | ||
| 219 | to search in, and the file name pattern to search for." | ||
| 220 | (interactive (list (project--read-regexp))) | ||
| 221 | (let* ((pr (project-current t)) | ||
| 222 | (dirs (if current-prefix-arg | ||
| 223 | (list (read-directory-name "Base directory: " | ||
| 224 | nil default-directory t)) | ||
| 225 | (project-roots pr)))) | ||
| 226 | (project--find-regexp-in dirs regexp pr))) | ||
| 227 | |||
| 228 | ;;;###autoload | ||
| 229 | (defun project-or-libraries-find-regexp (regexp) | ||
| 230 | "Find all matches for REGEXP in the current project or libraries. | ||
| 231 | With \\[universal-argument] prefix, you can specify the file name | ||
| 232 | pattern to search for." | ||
| 233 | (interactive (list (project--read-regexp))) | ||
| 234 | (let* ((pr (project-current t)) | ||
| 235 | (dirs (append | ||
| 236 | (project-roots pr) | ||
| 237 | (project-library-roots pr)))) | ||
| 238 | (project--find-regexp-in dirs regexp pr))) | ||
| 239 | |||
| 240 | (defun project--read-regexp () | ||
| 241 | (defvar xref-identifier-at-point-function) | ||
| 242 | (require 'xref) | ||
| 243 | (read-regexp "Find regexp" | ||
| 244 | (funcall xref-identifier-at-point-function))) | ||
| 245 | |||
| 246 | (defun project--find-regexp-in (dirs regexp project) | ||
| 247 | (require 'grep) | ||
| 248 | (let* ((files (if current-prefix-arg | ||
| 249 | (grep-read-files regexp) | ||
| 250 | "*")) | ||
| 251 | (xrefs (cl-mapcan | ||
| 252 | (lambda (dir) | ||
| 253 | (xref-collect-matches regexp files dir | ||
| 254 | (project-ignores project dir))) | ||
| 255 | dirs))) | ||
| 256 | (unless xrefs | ||
| 257 | (user-error "No matches for: %s" regexp)) | ||
| 258 | (xref--show-xrefs xrefs nil))) | ||
| 259 | |||
| 175 | (provide 'project) | 260 | (provide 'project) |
| 176 | ;;; project.el ends here | 261 | ;;; project.el ends here |
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index a222533936c..89a06046ca2 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el | |||
| @@ -414,20 +414,6 @@ WINDOW controls how the buffer is displayed: | |||
| 414 | (defvar-local xref--display-history nil | 414 | (defvar-local xref--display-history nil |
| 415 | "List of pairs (BUFFER . WINDOW), for temporarily displayed buffers.") | 415 | "List of pairs (BUFFER . WINDOW), for temporarily displayed buffers.") |
| 416 | 416 | ||
| 417 | (defvar-local xref--temporary-buffers nil | ||
| 418 | "List of buffers created by xref code.") | ||
| 419 | |||
| 420 | (defvar-local xref--current nil | ||
| 421 | "Non-nil if this buffer was once current, except while displaying xrefs. | ||
| 422 | Used for temporary buffers.") | ||
| 423 | |||
| 424 | (defvar xref--inhibit-mark-current nil) | ||
| 425 | |||
| 426 | (defun xref--mark-selected () | ||
| 427 | (unless xref--inhibit-mark-current | ||
| 428 | (setq xref--current t)) | ||
| 429 | (remove-hook 'buffer-list-update-hook #'xref--mark-selected t)) | ||
| 430 | |||
| 431 | (defun xref--save-to-history (buf win) | 417 | (defun xref--save-to-history (buf win) |
| 432 | (let ((restore (window-parameter win 'quit-restore))) | 418 | (let ((restore (window-parameter win 'quit-restore))) |
| 433 | ;; Save the new entry if the window displayed another buffer | 419 | ;; Save the new entry if the window displayed another buffer |
| @@ -449,15 +435,9 @@ Used for temporary buffers.") | |||
| 449 | 435 | ||
| 450 | (defun xref--show-location (location) | 436 | (defun xref--show-location (location) |
| 451 | (condition-case err | 437 | (condition-case err |
| 452 | (let ((bl (buffer-list)) | 438 | (let* ((marker (xref-location-marker location)) |
| 453 | (xref--inhibit-mark-current t) | 439 | (buf (marker-buffer marker))) |
| 454 | (marker (xref-location-marker location))) | 440 | (xref--display-position marker t buf)) |
| 455 | (let ((buf (marker-buffer marker))) | ||
| 456 | (unless (memq buf bl) | ||
| 457 | ;; Newly created. | ||
| 458 | (add-hook 'buffer-list-update-hook #'xref--mark-selected nil t) | ||
| 459 | (push buf xref--temporary-buffers)) | ||
| 460 | (xref--display-position marker t buf))) | ||
| 461 | (user-error (message (error-message-string err))))) | 441 | (user-error (message (error-message-string err))))) |
| 462 | 442 | ||
| 463 | (defun xref-show-location-at-point () | 443 | (defun xref-show-location-at-point () |
| @@ -594,8 +574,7 @@ Used for temporary buffers.") | |||
| 594 | (defun xref-quit (&optional kill) | 574 | (defun xref-quit (&optional kill) |
| 595 | "Bury temporarily displayed buffers, then quit the current window. | 575 | "Bury temporarily displayed buffers, then quit the current window. |
| 596 | 576 | ||
| 597 | If KILL is non-nil, kill all buffers that were created in the | 577 | If KILL is non-nil, also kill the current buffer. |
| 598 | process of showing xrefs, and also kill the current buffer. | ||
| 599 | 578 | ||
| 600 | The buffers that the user has otherwise interacted with in the | 579 | The buffers that the user has otherwise interacted with in the |
| 601 | meantime are preserved." | 580 | meantime are preserved." |
| @@ -607,13 +586,6 @@ meantime are preserved." | |||
| 607 | (when (and (window-live-p win) | 586 | (when (and (window-live-p win) |
| 608 | (eq buf (window-buffer win))) | 587 | (eq buf (window-buffer win))) |
| 609 | (quit-window nil win))) | 588 | (quit-window nil win))) |
| 610 | (when kill | ||
| 611 | (let ((xref--inhibit-mark-current t) | ||
| 612 | kill-buffer-query-functions) | ||
| 613 | (dolist (buf xref--temporary-buffers) | ||
| 614 | (unless (buffer-local-value 'xref--current buf) | ||
| 615 | (kill-buffer buf))) | ||
| 616 | (setq xref--temporary-buffers nil))) | ||
| 617 | (quit-window kill window))) | 589 | (quit-window kill window))) |
| 618 | 590 | ||
| 619 | (defconst xref-buffer-name "*xref*" | 591 | (defconst xref-buffer-name "*xref*" |
| @@ -687,15 +659,13 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)." | |||
| 687 | (pop-to-buffer (current-buffer)) | 659 | (pop-to-buffer (current-buffer)) |
| 688 | (goto-char (point-min)) | 660 | (goto-char (point-min)) |
| 689 | (setq xref--window (assoc-default 'window alist)) | 661 | (setq xref--window (assoc-default 'window alist)) |
| 690 | (setq xref--temporary-buffers (assoc-default 'temporary-buffers alist)) | ||
| 691 | (dolist (buf xref--temporary-buffers) | ||
| 692 | (with-current-buffer buf | ||
| 693 | (add-hook 'buffer-list-update-hook #'xref--mark-selected nil t))) | ||
| 694 | (current-buffer))))) | 662 | (current-buffer))))) |
| 695 | 663 | ||
| 696 | 664 | ||
| 697 | ;; This part of the UI seems fairly uncontroversial: it reads the | 665 | ;; This part of the UI seems fairly uncontroversial: it reads the |
| 698 | ;; identifier and deals with the single definition case. | 666 | ;; identifier and deals with the single definition case. |
| 667 | ;; (FIXME: do we really want this case to be handled like that in | ||
| 668 | ;; "find references" and "find regexp searches"?) | ||
| 699 | ;; | 669 | ;; |
| 700 | ;; The controversial multiple definitions case is handed off to | 670 | ;; The controversial multiple definitions case is handed off to |
| 701 | ;; xref-show-xrefs-function. | 671 | ;; xref-show-xrefs-function. |
| @@ -707,21 +677,15 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)." | |||
| 707 | 677 | ||
| 708 | (defvar xref--read-pattern-history nil) | 678 | (defvar xref--read-pattern-history nil) |
| 709 | 679 | ||
| 710 | (defun xref--show-xrefs (input kind arg window) | 680 | (defun xref--show-xrefs (xrefs window) |
| 711 | (let* ((bl (buffer-list)) | 681 | (cond |
| 712 | (xrefs (funcall xref-find-function kind arg)) | 682 | ((not (cdr xrefs)) |
| 713 | (tb (cl-set-difference (buffer-list) bl))) | 683 | (xref-push-marker-stack) |
| 714 | (cond | 684 | (xref--pop-to-location (car xrefs) window)) |
| 715 | ((null xrefs) | 685 | (t |
| 716 | (user-error "No %s found for: %s" (symbol-name kind) input)) | 686 | (xref-push-marker-stack) |
| 717 | ((not (cdr xrefs)) | 687 | (funcall xref-show-xrefs-function xrefs |
| 718 | (xref-push-marker-stack) | 688 | `((window . ,window)))))) |
| 719 | (xref--pop-to-location (car xrefs) window)) | ||
| 720 | (t | ||
| 721 | (xref-push-marker-stack) | ||
| 722 | (funcall xref-show-xrefs-function xrefs | ||
| 723 | `((window . ,window) | ||
| 724 | (temporary-buffers . ,tb))))))) | ||
| 725 | 689 | ||
| 726 | (defun xref--prompt-p (command) | 690 | (defun xref--prompt-p (command) |
| 727 | (or (eq xref-prompt-for-identifier t) | 691 | (or (eq xref-prompt-for-identifier t) |
| @@ -749,8 +713,14 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)." | |||
| 749 | 713 | ||
| 750 | ;;; Commands | 714 | ;;; Commands |
| 751 | 715 | ||
| 716 | (defun xref--find-xrefs (input kind arg window) | ||
| 717 | (let ((xrefs (funcall xref-find-function kind arg))) | ||
| 718 | (unless xrefs | ||
| 719 | (user-error "No %s found for: %s" (symbol-name kind) input)) | ||
| 720 | (xref--show-xrefs xrefs window))) | ||
| 721 | |||
| 752 | (defun xref--find-definitions (id window) | 722 | (defun xref--find-definitions (id window) |
| 753 | (xref--show-xrefs id 'definitions id window)) | 723 | (xref--find-xrefs id 'definitions id window)) |
| 754 | 724 | ||
| 755 | ;;;###autoload | 725 | ;;;###autoload |
| 756 | (defun xref-find-definitions (identifier) | 726 | (defun xref-find-definitions (identifier) |
| @@ -784,36 +754,7 @@ display the list in a buffer." | |||
| 784 | "Find references to the identifier at point. | 754 | "Find references to the identifier at point. |
| 785 | With prefix argument, prompt for the identifier." | 755 | With prefix argument, prompt for the identifier." |
| 786 | (interactive (list (xref--read-identifier "Find references of: "))) | 756 | (interactive (list (xref--read-identifier "Find references of: "))) |
| 787 | (xref--show-xrefs identifier 'references identifier nil)) | 757 | (xref--find-xrefs identifier 'references identifier nil)) |
| 788 | |||
| 789 | ;; TODO: Rename and move to project-find-regexp, as soon as idiomatic | ||
| 790 | ;; usage of xref from other packages has stabilized. | ||
| 791 | ;;;###autoload | ||
| 792 | (defun xref-find-regexp (regexp) | ||
| 793 | "Find all matches for REGEXP. | ||
| 794 | With \\[universal-argument] prefix, you can specify the directory | ||
| 795 | to search in, and the file name pattern to search for." | ||
| 796 | (interactive (list (xref--read-identifier "Find regexp: "))) | ||
| 797 | (require 'grep) | ||
| 798 | (let* ((proj (project-current)) | ||
| 799 | (files (if current-prefix-arg | ||
| 800 | (grep-read-files regexp) | ||
| 801 | "*")) | ||
| 802 | (dirs (if current-prefix-arg | ||
| 803 | (list (read-directory-name "Base directory: " | ||
| 804 | nil default-directory t)) | ||
| 805 | (project-prune-directories | ||
| 806 | (append | ||
| 807 | (project-roots proj) | ||
| 808 | (project-search-path proj))))) | ||
| 809 | (xref-find-function | ||
| 810 | (lambda (_kind regexp) | ||
| 811 | (cl-mapcan | ||
| 812 | (lambda (dir) | ||
| 813 | (xref-collect-matches regexp files dir | ||
| 814 | (project-ignores proj dir))) | ||
| 815 | dirs)))) | ||
| 816 | (xref--show-xrefs regexp 'matches regexp nil))) | ||
| 817 | 758 | ||
| 818 | (declare-function apropos-parse-pattern "apropos" (pattern)) | 759 | (declare-function apropos-parse-pattern "apropos" (pattern)) |
| 819 | 760 | ||
| @@ -825,7 +766,7 @@ The argument has the same meaning as in `apropos'." | |||
| 825 | "Search for pattern (word list or regexp): " | 766 | "Search for pattern (word list or regexp): " |
| 826 | nil 'xref--read-pattern-history))) | 767 | nil 'xref--read-pattern-history))) |
| 827 | (require 'apropos) | 768 | (require 'apropos) |
| 828 | (xref--show-xrefs pattern 'apropos | 769 | (xref--find-xrefs pattern 'apropos |
| 829 | (apropos-parse-pattern | 770 | (apropos-parse-pattern |
| 830 | (if (string-equal (regexp-quote pattern) pattern) | 771 | (if (string-equal (regexp-quote pattern) pattern) |
| 831 | ;; Split into words | 772 | ;; Split into words |
| @@ -869,7 +810,6 @@ and just use etags." | |||
| 869 | 810 | ||
| 870 | (declare-function semantic-symref-find-references-by-name "semantic/symref") | 811 | (declare-function semantic-symref-find-references-by-name "semantic/symref") |
| 871 | (declare-function semantic-find-file-noselect "semantic/fw") | 812 | (declare-function semantic-find-file-noselect "semantic/fw") |
| 872 | (declare-function grep-read-files "grep") | ||
| 873 | (declare-function grep-expand-template "grep") | 813 | (declare-function grep-expand-template "grep") |
| 874 | 814 | ||
| 875 | (defun xref-collect-references (symbol dir) | 815 | (defun xref-collect-references (symbol dir) |
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 28ddeb3d58b..efd816b4f0e 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el | |||
| @@ -1433,8 +1433,9 @@ Argument BACKEND is the backend you are using." | |||
| 1433 | (lambda (str) | 1433 | (lambda (str) |
| 1434 | ;; Commented or empty lines. | 1434 | ;; Commented or empty lines. |
| 1435 | (string-match-p "\\`\\(?:#\\|[ \t\r\n]*\\'\\)" str)) | 1435 | (string-match-p "\\`\\(?:#\\|[ \t\r\n]*\\'\\)" str)) |
| 1436 | (vc--read-lines | 1436 | (let ((file (vc-call-backend backend 'find-ignore-file file))) |
| 1437 | (vc-call-backend backend 'find-ignore-file file)))) | 1437 | (and (file-exists-p file) |
| 1438 | (vc--read-lines file))))) | ||
| 1438 | 1439 | ||
| 1439 | (defun vc--read-lines (file) | 1440 | (defun vc--read-lines (file) |
| 1440 | "Return a list of lines of FILE." | 1441 | "Return a list of lines of FILE." |
diff --git a/test/automated/cl-lib-tests.el b/test/automated/cl-lib-tests.el index b31622fdc33..9b230db99e4 100644 --- a/test/automated/cl-lib-tests.el +++ b/test/automated/cl-lib-tests.el | |||
| @@ -117,8 +117,8 @@ | |||
| 117 | (should (equal (cl-set-difference b b) e)) | 117 | (should (equal (cl-set-difference b b) e)) |
| 118 | ;; Note: this test (and others) is sensitive to the order of the | 118 | ;; Note: this test (and others) is sensitive to the order of the |
| 119 | ;; result, which is not documented. | 119 | ;; result, which is not documented. |
| 120 | (should (equal (cl-set-difference a b) (list c2 "x" "" nil 'a))) | 120 | (should (equal (cl-set-difference a b) (list 'a nil "" "x" c2))) |
| 121 | (should (equal (cl-set-difference b a) (list 'x 'y))) | 121 | (should (equal (cl-set-difference b a) (list 'y 'x))) |
| 122 | 122 | ||
| 123 | ;; We aren't testing whether this is really using `eq' rather than `eql'. | 123 | ;; We aren't testing whether this is really using `eq' rather than `eql'. |
| 124 | (should (equal (cl-set-difference e e :test 'eq) e)) | 124 | (should (equal (cl-set-difference e e :test 'eq) e)) |
| @@ -128,8 +128,8 @@ | |||
| 128 | (should (equal (cl-set-difference b e :test 'eq) b)) | 128 | (should (equal (cl-set-difference b e :test 'eq) b)) |
| 129 | (should (equal (cl-set-difference e b :test 'eq) e)) | 129 | (should (equal (cl-set-difference e b :test 'eq) e)) |
| 130 | (should (equal (cl-set-difference b b :test 'eq) e)) | 130 | (should (equal (cl-set-difference b b :test 'eq) e)) |
| 131 | (should (equal (cl-set-difference a b :test 'eq) (list c2 "x" "" nil 'a))) | 131 | (should (equal (cl-set-difference a b :test 'eq) (list 'a nil "" "x" c2))) |
| 132 | (should (equal (cl-set-difference b a :test 'eq) (list 'x 'y))) | 132 | (should (equal (cl-set-difference b a :test 'eq) (list 'y 'x))) |
| 133 | 133 | ||
| 134 | (should (equal (cl-union e e) e)) | 134 | (should (equal (cl-union e e) e)) |
| 135 | (should (equal (cl-union a e) a)) | 135 | (should (equal (cl-union a e) a)) |