aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2015-11-10 02:47:46 +0200
committerDmitry Gutov2015-11-10 02:47:46 +0200
commit0be6fb8e17f708fe03430d0b1e701810ae51b5e3 (patch)
tree03abad9d6697b70fbc6f26e5504c7c20ae070f3d
parent3c3aad733522365a8fe729d7c92e64e98bc4ce92 (diff)
parent1c72afb7aa48c2ea06103113ef70ccea0c1c961d (diff)
downloademacs-0be6fb8e17f708fe03430d0b1e701810ae51b5e3.tar.gz
emacs-0be6fb8e17f708fe03430d0b1e701810ae51b5e3.zip
Merge branch 'project-next'
-rw-r--r--lisp/emacs-lisp/cl-seq.el2
-rw-r--r--lisp/progmodes/elisp-mode.el13
-rw-r--r--lisp/progmodes/etags.el7
-rw-r--r--lisp/progmodes/project.el197
-rw-r--r--lisp/progmodes/xref.el108
-rw-r--r--lisp/vc/vc.el5
-rw-r--r--test/automated/cl-lib-tests.el8
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.
37Each functions on this hook is called in turn with one 36Each functions on this hook is called in turn with one
38argument (the directory) and should return either nil to mean 37argument (the directory) and should return either nil to mean
39that it is not applicable, or a project instance.") 38that 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
46The directories in which we can recursively look for the 60It should return a list of directories that contain source files
47declarations or other references to the symbols used in the 61related to the current buffer. Depending on the language, it
48current buffer. Depending on the language, it should include the 62should include the headers search path, load path, class path,
49headers search path, load path, class path, or so on. 63and so on.
50 64
51The directory names should be absolute. This variable is 65The directory names should be absolute. Used in the default
52normally set by the major mode. Used in the default 66implementation of `project-library-roots'.")
53implementation 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'.
71When no project found in DIR, and MAYBE-PROMPT is non-nil, ask
72the 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.
64Any directory roots where source (or header, etc) files used by 90
65the current project may be found, inside or outside of the 91It's the list of directories outside of the project that contain
66current project tree(s). The directory names should be absolute. 92related source files.
67 93
68Unless it really knows better, a specialized implementation 94Project-specific version of `project-library-roots-function',
69should take into account the value returned by 95which see. Unless it knows better, a specialized implementation
70`project-search-path-function' and call 96should 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.
81It should include the current project root, as well as the roots 104
82of any other currently open projects, if they're meant to be 105Most often it's just one directory, which contains the project
83edited together. The directory names should be absolute.") 106file and everything else in the project. But in more advanced
107configurations, a project can span multiple directories.
108
109The rule of tumb for whether to include a directory here, and not
110in `project-library-roots', is whether its contents are meant to
111be edited together with the rest of the project.
112
113The 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.
87Patterns can match both regular files and directories. 117Patterns can match both regular files and directories.
88To root an entry, start it with `./'. To match directories only, 118To root an entry, start it with `./'. To match directories only,
89end it with `/'. DIR must be either one of `project-roots', or 119end it with `/'. DIR must be one of `project-roots' or
90an 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'.
106The file names can be absolute, or relative to the project root." 136The 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 182Appends the elements of LISTS-OF-DIRS together, removes
150(cl-defmethod project-roots ((project (head user))) 183non-existing directories, as well as directories a parent of
151 (list (cdr project))) 184whose 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.
201DIRS 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.
218With \\[universal-argument] prefix, you can specify the directory
219to 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.
231With \\[universal-argument] prefix, you can specify the file name
232pattern 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.
422Used 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
597If KILL is non-nil, kill all buffers that were created in the 577If KILL is non-nil, also kill the current buffer.
598process of showing xrefs, and also kill the current buffer.
599 578
600The buffers that the user has otherwise interacted with in the 579The buffers that the user has otherwise interacted with in the
601meantime are preserved." 580meantime 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.
785With prefix argument, prompt for the identifier." 755With 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.
794With \\[universal-argument] prefix, you can specify the directory
795to 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))