aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2016-04-12 21:08:22 +0300
committerDmitry Gutov2016-04-12 21:08:56 +0300
commitcc0b713210a4ae3e273571f8c829122db2b143cf (patch)
treee511f08acafd5d691a4943b2c90163213fa828c6
parent50455754b558d1689bbbb596e05631f2789b1350 (diff)
downloademacs-cc0b713210a4ae3e273571f8c829122db2b143cf.tar.gz
emacs-cc0b713210a4ae3e273571f8c829122db2b143cf.zip
Perform xref searches without visiting unopened files
* lisp/progmodes/xref.el (xref-collect-references): Instead of calling `semantic-symref-find-references-by-name', use `semantic-symref-instantiate' and `semantic-symref-perform-search' directly. Ask for `line-and-text' results (bug#23223). (xref-collect-matches): Include the line text in the "hit" structure. (xref--convert-hits): New function, split off from `xref-collect-references' and `xref-collect-matches', to convert "hits" to xref instance list. Create a temporary buffer here, to use it for post-processing all hit lines. (xref--collect-matches): Use a different approach for non-visited files. Insert the line text into the temp buffer, apply the file's major mode the best we can without reading its whole contents, syntax-propertize, and search in the result. (xref--collect-matches-1): Extract, to handle the common logic between two cases. (xref--find-buffer-visiting): New function, a wrapper around `find-buffer-visiting' to amortize its cost. * lisp/cedet/semantic/symref/idutils.el (semantic-symref-idutils--line-re): New constant. (semantic-symref-parse-tool-output-one-line): Support result type `line-and-text'. * lisp/cedet/semantic/symref/grep.el (semantic-symref-grep--line-re) (semantic-symref-parse-tool-output-one-line): Same. * lisp/cedet/semantic/symref/cscope.el (semantic-symref-cscope--line-re) (semantic-symref-parse-tool-output-one-line): Same. * lisp/cedet/semantic/symref/global.el (semantic-symref-global--line-re) (semantic-symref-parse-tool-output-one-line): Same.
-rw-r--r--lisp/cedet/semantic/symref/cscope.el12
-rw-r--r--lisp/cedet/semantic/symref/global.el10
-rw-r--r--lisp/cedet/semantic/symref/grep.el10
-rw-r--r--lisp/cedet/semantic/symref/idutils.el12
-rw-r--r--lisp/progmodes/xref.el140
5 files changed, 121 insertions, 63 deletions
diff --git a/lisp/cedet/semantic/symref/cscope.el b/lisp/cedet/semantic/symref/cscope.el
index 4890b5b5755..3abd8b3f51c 100644
--- a/lisp/cedet/semantic/symref/cscope.el
+++ b/lisp/cedet/semantic/symref/cscope.el
@@ -60,6 +60,9 @@ See the function `cedet-cscope-search' for more details.")
60 (semantic-symref-parse-tool-output tool b) 60 (semantic-symref-parse-tool-output tool b)
61 )) 61 ))
62 62
63(defconst semantic-symref-cscope--line-re
64 "^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) ")
65
63(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-cscope)) 66(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-cscope))
64 "Parse one line of grep output, and return it as a match list. 67 "Parse one line of grep output, and return it as a match list.
65Moves cursor to end of the match." 68Moves cursor to end of the match."
@@ -78,8 +81,13 @@ Moves cursor to end of the match."
78 ;; We have to return something at this point. 81 ;; We have to return something at this point.
79 subtxt))) 82 subtxt)))
80 ) 83 )
81 (t 84 ((eq (oref tool :resulttype) 'line-and-text)
82 (when (re-search-forward "^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) " nil t) 85 (when (re-search-forward semantic-symref-cscope--line-re nil t)
86 (list (string-to-number (match-string 2))
87 (expand-file-name (match-string 1))
88 (buffer-substring-no-properties (point) (line-end-position)))))
89 (t ; :resulttype is 'line
90 (when (re-search-forward semantic-symref-cscope--line-re nil t)
83 (cons (string-to-number (match-string 2)) 91 (cons (string-to-number (match-string 2))
84 (expand-file-name (match-string 1))) 92 (expand-file-name (match-string 1)))
85 )))) 93 ))))
diff --git a/lisp/cedet/semantic/symref/global.el b/lisp/cedet/semantic/symref/global.el
index e4c114e9c89..a33427e93a6 100644
--- a/lisp/cedet/semantic/symref/global.el
+++ b/lisp/cedet/semantic/symref/global.el
@@ -49,6 +49,9 @@ See the function `cedet-gnu-global-search' for more details.")
49 (semantic-symref-parse-tool-output tool b) 49 (semantic-symref-parse-tool-output tool b)
50 )) 50 ))
51 51
52(defconst semantic-symref-global--line-re
53 "^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) ")
54
52(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-global)) 55(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-global))
53 "Parse one line of grep output, and return it as a match list. 56 "Parse one line of grep output, and return it as a match list.
54Moves cursor to end of the match." 57Moves cursor to end of the match."
@@ -57,8 +60,13 @@ Moves cursor to end of the match."
57 ;; Search for files 60 ;; Search for files
58 (when (re-search-forward "^\\([^\n]+\\)$" nil t) 61 (when (re-search-forward "^\\([^\n]+\\)$" nil t)
59 (match-string 1))) 62 (match-string 1)))
63 ((eq (oref tool :resulttype) 'line-and-text)
64 (when (re-search-forward semantic-symref-global--line-re nil t)
65 (list (string-to-number (match-string 2))
66 (match-string 3)
67 (buffer-substring-no-properties (point) (line-end-position)))))
60 (t 68 (t
61 (when (re-search-forward "^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) " nil t) 69 (when (re-search-forward semantic-symref-global--line-re nil t)
62 (cons (string-to-number (match-string 2)) 70 (cons (string-to-number (match-string 2))
63 (match-string 3)) 71 (match-string 3))
64 )))) 72 ))))
diff --git a/lisp/cedet/semantic/symref/grep.el b/lisp/cedet/semantic/symref/grep.el
index 5d1fea8c829..868e6c3f726 100644
--- a/lisp/cedet/semantic/symref/grep.el
+++ b/lisp/cedet/semantic/symref/grep.el
@@ -188,6 +188,9 @@ This shell should support pipe redirect syntax."
188 ;; Return the answer 188 ;; Return the answer
189 ans)) 189 ans))
190 190
191(defconst semantic-symref-grep--line-re
192 "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):")
193
191(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep)) 194(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep))
192 "Parse one line of grep output, and return it as a match list. 195 "Parse one line of grep output, and return it as a match list.
193Moves cursor to end of the match." 196Moves cursor to end of the match."
@@ -195,8 +198,13 @@ Moves cursor to end of the match."
195 ;; Search for files 198 ;; Search for files
196 (when (re-search-forward "^\\([^\n]+\\)$" nil t) 199 (when (re-search-forward "^\\([^\n]+\\)$" nil t)
197 (match-string 1))) 200 (match-string 1)))
201 ((eq (oref tool :resulttype) 'line-and-text)
202 (when (re-search-forward semantic-symref-grep--line-re nil t)
203 (list (string-to-number (match-string 2))
204 (match-string 1)
205 (buffer-substring-no-properties (point) (line-end-position)))))
198 (t 206 (t
199 (when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t) 207 (when (re-search-forward semantic-symref-grep--line-re nil t)
200 (cons (string-to-number (match-string 2)) 208 (cons (string-to-number (match-string 2))
201 (match-string 1)) 209 (match-string 1))
202 )))) 210 ))))
diff --git a/lisp/cedet/semantic/symref/idutils.el b/lisp/cedet/semantic/symref/idutils.el
index 4127d7ae4ea..db3e9a0dddb 100644
--- a/lisp/cedet/semantic/symref/idutils.el
+++ b/lisp/cedet/semantic/symref/idutils.el
@@ -49,6 +49,9 @@ See the function `cedet-idutils-search' for more details.")
49 (semantic-symref-parse-tool-output tool b) 49 (semantic-symref-parse-tool-output tool b)
50 )) 50 ))
51 51
52(defconst semantic-symref-idutils--line-re
53 "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):")
54
52(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-idutils)) 55(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-idutils))
53 "Parse one line of grep output, and return it as a match list. 56 "Parse one line of grep output, and return it as a match list.
54Moves cursor to end of the match." 57Moves cursor to end of the match."
@@ -59,8 +62,13 @@ Moves cursor to end of the match."
59 ((eq (oref tool :searchtype) 'tagcompletions) 62 ((eq (oref tool :searchtype) 'tagcompletions)
60 (when (re-search-forward "^\\([^ ]+\\) " nil t) 63 (when (re-search-forward "^\\([^ ]+\\) " nil t)
61 (match-string 1))) 64 (match-string 1)))
62 (t 65 ((eq (oref tool :resulttype) 'line-and-text)
63 (when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t) 66 (when (re-search-forward semantic-symref-idutils--line-re nil t)
67 (list (string-to-number (match-string 2))
68 (expand-file-name (match-string 1) default-directory)
69 (buffer-substring-no-properties (point) (line-end-position)))))
70 (t ; resulttype is line
71 (when (re-search-forward semantic-symref-idutils--line-re nil t)
64 (cons (string-to-number (match-string 2)) 72 (cons (string-to-number (match-string 2))
65 (expand-file-name (match-string 1) default-directory)) 73 (expand-file-name (match-string 1) default-directory))
66 )))) 74 ))))
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index feed0fb36d9..f674c70b104 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -839,16 +839,16 @@ and just use etags."
839 (kill-local-variable 'xref-backend-functions)) 839 (kill-local-variable 'xref-backend-functions))
840 (setq-local xref-backend-functions xref-etags-mode--saved))) 840 (setq-local xref-backend-functions xref-etags-mode--saved)))
841 841
842(declare-function semantic-symref-find-references-by-name "semantic/symref") 842(declare-function semantic-symref-instantiate "semantic/symref")
843(declare-function semantic-find-file-noselect "semantic/fw") 843(declare-function semantic-symref-perform-search "semantic/symref")
844(declare-function grep-expand-template "grep") 844(declare-function grep-expand-template "grep")
845(defvar ede-minor-mode) ;; ede.el 845(defvar ede-minor-mode) ;; ede.el
846 846
847(defun xref-collect-references (symbol dir) 847(defun xref-collect-references (symbol dir)
848 "Collect references to SYMBOL inside DIR. 848 "Collect references to SYMBOL inside DIR.
849This function uses the Semantic Symbol Reference API, see 849This function uses the Semantic Symbol Reference API, see
850`semantic-symref-find-references-by-name' for details on which 850`semantic-symref-tool-alist' for details on which tools are used,
851tools are used, and when." 851and when."
852 (cl-assert (directory-name-p dir)) 852 (cl-assert (directory-name-p dir))
853 (require 'semantic/symref) 853 (require 'semantic/symref)
854 (defvar semantic-symref-tool) 854 (defvar semantic-symref-tool)
@@ -859,19 +859,19 @@ tools are used, and when."
859 ;; to force the backend to use `default-directory'. 859 ;; to force the backend to use `default-directory'.
860 (let* ((ede-minor-mode nil) 860 (let* ((ede-minor-mode nil)
861 (default-directory dir) 861 (default-directory dir)
862 ;; FIXME: Remove CScope and Global from the recognized tools?
863 ;; The current implementations interpret the symbol search as
864 ;; "find all calls to the given function", but not function
865 ;; definition. And they return nothing when passed a variable
866 ;; name, even a global one.
862 (semantic-symref-tool 'detect) 867 (semantic-symref-tool 'detect)
863 (case-fold-search nil) 868 (case-fold-search nil)
864 (res (semantic-symref-find-references-by-name symbol 'subdirs)) 869 (inst (semantic-symref-instantiate :searchfor symbol
865 (hits (and res (oref res hit-lines))) 870 :searchtype 'symbol
866 (orig-buffers (buffer-list))) 871 :searchscope 'subdirs
867 (unwind-protect 872 :resulttype 'line-and-text)))
868 (cl-mapcan (lambda (hit) (xref--collect-matches 873 (xref--convert-hits (semantic-symref-perform-search inst)
869 hit (format "\\_<%s\\_>" (regexp-quote symbol)))) 874 (format "\\_<%s\\_>" (regexp-quote symbol)))))
870 hits)
871 ;; TODO: Implement "lightweight" buffer visiting, so that we
872 ;; don't have to kill them.
873 (mapc #'kill-buffer
874 (cl-set-difference (buffer-list) orig-buffers)))))
875 875
876;;;###autoload 876;;;###autoload
877(defun xref-collect-matches (regexp files dir ignores) 877(defun xref-collect-matches (regexp files dir ignores)
@@ -890,34 +890,19 @@ IGNORES is a list of glob patterns."
890 files 890 files
891 (expand-file-name dir) 891 (expand-file-name dir)
892 ignores)) 892 ignores))
893 (orig-buffers (buffer-list))
894 (buf (get-buffer-create " *xref-grep*")) 893 (buf (get-buffer-create " *xref-grep*"))
895 (grep-re (caar grep-regexp-alist)) 894 (grep-re (caar grep-regexp-alist))
896 (counter 0)
897 reporter
898 hits) 895 hits)
899 (with-current-buffer buf 896 (with-current-buffer buf
900 (erase-buffer) 897 (erase-buffer)
901 (call-process-shell-command command nil t) 898 (call-process-shell-command command nil t)
902 (goto-char (point-min)) 899 (goto-char (point-min))
903 (while (re-search-forward grep-re nil t) 900 (while (re-search-forward grep-re nil t)
904 (push (cons (string-to-number (match-string 2)) 901 (push (list (string-to-number (match-string 2))
905 (match-string 1)) 902 (match-string 1)
903 (buffer-substring-no-properties (point) (line-end-position)))
906 hits))) 904 hits)))
907 (setq reporter (make-progress-reporter 905 (xref--convert-hits hits regexp)))
908 (format "Collecting search results...")
909 0 (length hits)))
910 (unwind-protect
911 (cl-mapcan (lambda (hit)
912 (prog1
913 (progress-reporter-update reporter counter)
914 (cl-incf counter))
915 (xref--collect-matches hit regexp))
916 (nreverse hits))
917 (progress-reporter-done reporter)
918 ;; TODO: Same as above.
919 (mapc #'kill-buffer
920 (cl-set-difference (buffer-list) orig-buffers)))))
921 906
922(defun xref--rgrep-command (regexp files dir ignores) 907(defun xref--rgrep-command (regexp files dir ignores)
923 (require 'find-dired) ; for `find-name-arg' 908 (require 'find-dired) ; for `find-name-arg'
@@ -980,30 +965,71 @@ directory, used as the root of the ignore globs."
980 (match-string 1 str))))) 965 (match-string 1 str)))))
981 str t t)) 966 str t t))
982 967
983(defun xref--collect-matches (hit regexp) 968(defvar xref--last-visiting-buffer nil)
984 (pcase-let* ((`(,line . ,file) hit) 969(defvar xref--temp-buffer-file-name nil)
985 (buf (or (find-buffer-visiting file) 970
986 (semantic-find-file-noselect file)))) 971(defun xref--convert-hits (hits regexp)
987 (with-current-buffer buf 972 (let (xref--last-visiting-buffer
988 (save-excursion 973 (tmp-buffer (generate-new-buffer " *xref-temp*")))
974 (unwind-protect
975 (cl-mapcan (lambda (hit) (xref--collect-matches hit regexp tmp-buffer))
976 hits)
977 (kill-buffer tmp-buffer))))
978
979(defun xref--collect-matches (hit regexp tmp-buffer)
980 (pcase-let* ((`(,line ,file ,text) hit)
981 (buf (xref--find-buffer-visiting file)))
982 (if buf
983 (with-current-buffer buf
984 (save-excursion
985 (goto-char (point-min))
986 (forward-line (1- line))
987 (xref--collect-matches-1 regexp file line
988 (line-beginning-position)
989 (line-end-position))))
990 ;; Using the temporary buffer is both a performance and a buffer
991 ;; management optimization.
992 (with-current-buffer tmp-buffer
993 (erase-buffer)
994 (unless (equal file xref--temp-buffer-file-name)
995 (insert-file-contents file nil 0 200)
996 ;; Can't (setq-local delay-mode-hooks t) because of
997 ;; bug#23272, but the performance penalty seems minimal.
998 (let ((buffer-file-name file)
999 (inhibit-message t)
1000 message-log-max)
1001 (ignore-errors
1002 (set-auto-mode t)))
1003 (setq-local xref--temp-buffer-file-name file)
1004 (setq-local inhibit-read-only t)
1005 (erase-buffer))
1006 (insert text)
989 (goto-char (point-min)) 1007 (goto-char (point-min))
990 (forward-line (1- line)) 1008 (xref--collect-matches-1 regexp file line
991 (let ((line-end (line-end-position)) 1009 (point)
992 (line-beg (line-beginning-position)) 1010 (point-max))))))
993 matches) 1011
994 (syntax-propertize line-end) 1012(defun xref--collect-matches-1 (regexp file line line-beg line-end)
995 ;; FIXME: This results in several lines with the same 1013 (let (matches)
996 ;; summary. Solve with composite pattern? 1014 (syntax-propertize line-end)
997 (while (re-search-forward regexp line-end t) 1015 ;; FIXME: This results in several lines with the same
998 (let* ((beg-column (- (match-beginning 0) line-beg)) 1016 ;; summary. Solve with composite pattern?
999 (end-column (- (match-end 0) line-beg)) 1017 (while (re-search-forward regexp line-end t)
1000 (loc (xref-make-file-location file line beg-column)) 1018 (let* ((beg-column (- (match-beginning 0) line-beg))
1001 (summary (buffer-substring line-beg line-end))) 1019 (end-column (- (match-end 0) line-beg))
1002 (add-face-text-property beg-column end-column 'highlight 1020 (loc (xref-make-file-location file line beg-column))
1003 t summary) 1021 (summary (buffer-substring line-beg line-end)))
1004 (push (xref-make-match summary loc (- end-column beg-column)) 1022 (add-face-text-property beg-column end-column 'highlight
1005 matches))) 1023 t summary)
1006 (nreverse matches)))))) 1024 (push (xref-make-match summary loc (- end-column beg-column))
1025 matches)))
1026 (nreverse matches)))
1027
1028(defun xref--find-buffer-visiting (file)
1029 (unless (equal (car xref--last-visiting-buffer) file)
1030 (setq xref--last-visiting-buffer
1031 (cons file (find-buffer-visiting file))))
1032 (cdr xref--last-visiting-buffer))
1007 1033
1008(provide 'xref) 1034(provide 'xref)
1009 1035