diff options
| author | Dmitry Gutov | 2016-04-12 21:08:22 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2016-04-12 21:08:56 +0300 |
| commit | cc0b713210a4ae3e273571f8c829122db2b143cf (patch) | |
| tree | e511f08acafd5d691a4943b2c90163213fa828c6 | |
| parent | 50455754b558d1689bbbb596e05631f2789b1350 (diff) | |
| download | emacs-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.el | 12 | ||||
| -rw-r--r-- | lisp/cedet/semantic/symref/global.el | 10 | ||||
| -rw-r--r-- | lisp/cedet/semantic/symref/grep.el | 10 | ||||
| -rw-r--r-- | lisp/cedet/semantic/symref/idutils.el | 12 | ||||
| -rw-r--r-- | lisp/progmodes/xref.el | 140 |
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. |
| 65 | Moves cursor to end of the match." | 68 | Moves 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. |
| 54 | Moves cursor to end of the match." | 57 | Moves 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. |
| 193 | Moves cursor to end of the match." | 196 | Moves 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. |
| 54 | Moves cursor to end of the match." | 57 | Moves 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. |
| 849 | This function uses the Semantic Symbol Reference API, see | 849 | This 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, |
| 851 | tools are used, and when." | 851 | and 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 | ||