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.
This commit is contained in:
parent
50455754b5
commit
cc0b713210
5 changed files with 121 additions and 63 deletions
|
@ -60,6 +60,9 @@ See the function `cedet-cscope-search' for more details.")
|
||||||
(semantic-symref-parse-tool-output tool b)
|
(semantic-symref-parse-tool-output tool b)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
(defconst semantic-symref-cscope--line-re
|
||||||
|
"^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) ")
|
||||||
|
|
||||||
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-cscope))
|
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-cscope))
|
||||||
"Parse one line of grep output, and return it as a match list.
|
"Parse one line of grep output, and return it as a match list.
|
||||||
Moves cursor to end of the match."
|
Moves cursor to end of the match."
|
||||||
|
@ -78,8 +81,13 @@ Moves cursor to end of the match."
|
||||||
;; We have to return something at this point.
|
;; We have to return something at this point.
|
||||||
subtxt)))
|
subtxt)))
|
||||||
)
|
)
|
||||||
(t
|
((eq (oref tool :resulttype) 'line-and-text)
|
||||||
(when (re-search-forward "^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) " nil t)
|
(when (re-search-forward semantic-symref-cscope--line-re nil t)
|
||||||
|
(list (string-to-number (match-string 2))
|
||||||
|
(expand-file-name (match-string 1))
|
||||||
|
(buffer-substring-no-properties (point) (line-end-position)))))
|
||||||
|
(t ; :resulttype is 'line
|
||||||
|
(when (re-search-forward semantic-symref-cscope--line-re nil t)
|
||||||
(cons (string-to-number (match-string 2))
|
(cons (string-to-number (match-string 2))
|
||||||
(expand-file-name (match-string 1)))
|
(expand-file-name (match-string 1)))
|
||||||
))))
|
))))
|
||||||
|
|
|
@ -49,6 +49,9 @@ See the function `cedet-gnu-global-search' for more details.")
|
||||||
(semantic-symref-parse-tool-output tool b)
|
(semantic-symref-parse-tool-output tool b)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
(defconst semantic-symref-global--line-re
|
||||||
|
"^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) ")
|
||||||
|
|
||||||
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-global))
|
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-global))
|
||||||
"Parse one line of grep output, and return it as a match list.
|
"Parse one line of grep output, and return it as a match list.
|
||||||
Moves cursor to end of the match."
|
Moves cursor to end of the match."
|
||||||
|
@ -57,8 +60,13 @@ Moves cursor to end of the match."
|
||||||
;; Search for files
|
;; Search for files
|
||||||
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
||||||
(match-string 1)))
|
(match-string 1)))
|
||||||
|
((eq (oref tool :resulttype) 'line-and-text)
|
||||||
|
(when (re-search-forward semantic-symref-global--line-re nil t)
|
||||||
|
(list (string-to-number (match-string 2))
|
||||||
|
(match-string 3)
|
||||||
|
(buffer-substring-no-properties (point) (line-end-position)))))
|
||||||
(t
|
(t
|
||||||
(when (re-search-forward "^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) " nil t)
|
(when (re-search-forward semantic-symref-global--line-re nil t)
|
||||||
(cons (string-to-number (match-string 2))
|
(cons (string-to-number (match-string 2))
|
||||||
(match-string 3))
|
(match-string 3))
|
||||||
))))
|
))))
|
||||||
|
|
|
@ -188,6 +188,9 @@ This shell should support pipe redirect syntax."
|
||||||
;; Return the answer
|
;; Return the answer
|
||||||
ans))
|
ans))
|
||||||
|
|
||||||
|
(defconst semantic-symref-grep--line-re
|
||||||
|
"^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):")
|
||||||
|
|
||||||
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep))
|
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-grep))
|
||||||
"Parse one line of grep output, and return it as a match list.
|
"Parse one line of grep output, and return it as a match list.
|
||||||
Moves cursor to end of the match."
|
Moves cursor to end of the match."
|
||||||
|
@ -195,8 +198,13 @@ Moves cursor to end of the match."
|
||||||
;; Search for files
|
;; Search for files
|
||||||
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
||||||
(match-string 1)))
|
(match-string 1)))
|
||||||
|
((eq (oref tool :resulttype) 'line-and-text)
|
||||||
|
(when (re-search-forward semantic-symref-grep--line-re nil t)
|
||||||
|
(list (string-to-number (match-string 2))
|
||||||
|
(match-string 1)
|
||||||
|
(buffer-substring-no-properties (point) (line-end-position)))))
|
||||||
(t
|
(t
|
||||||
(when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t)
|
(when (re-search-forward semantic-symref-grep--line-re nil t)
|
||||||
(cons (string-to-number (match-string 2))
|
(cons (string-to-number (match-string 2))
|
||||||
(match-string 1))
|
(match-string 1))
|
||||||
))))
|
))))
|
||||||
|
|
|
@ -49,6 +49,9 @@ See the function `cedet-idutils-search' for more details.")
|
||||||
(semantic-symref-parse-tool-output tool b)
|
(semantic-symref-parse-tool-output tool b)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
(defconst semantic-symref-idutils--line-re
|
||||||
|
"^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):")
|
||||||
|
|
||||||
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-idutils))
|
(cl-defmethod semantic-symref-parse-tool-output-one-line ((tool semantic-symref-tool-idutils))
|
||||||
"Parse one line of grep output, and return it as a match list.
|
"Parse one line of grep output, and return it as a match list.
|
||||||
Moves cursor to end of the match."
|
Moves cursor to end of the match."
|
||||||
|
@ -59,8 +62,13 @@ Moves cursor to end of the match."
|
||||||
((eq (oref tool :searchtype) 'tagcompletions)
|
((eq (oref tool :searchtype) 'tagcompletions)
|
||||||
(when (re-search-forward "^\\([^ ]+\\) " nil t)
|
(when (re-search-forward "^\\([^ ]+\\) " nil t)
|
||||||
(match-string 1)))
|
(match-string 1)))
|
||||||
(t
|
((eq (oref tool :resulttype) 'line-and-text)
|
||||||
(when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t)
|
(when (re-search-forward semantic-symref-idutils--line-re nil t)
|
||||||
|
(list (string-to-number (match-string 2))
|
||||||
|
(expand-file-name (match-string 1) default-directory)
|
||||||
|
(buffer-substring-no-properties (point) (line-end-position)))))
|
||||||
|
(t ; resulttype is line
|
||||||
|
(when (re-search-forward semantic-symref-idutils--line-re nil t)
|
||||||
(cons (string-to-number (match-string 2))
|
(cons (string-to-number (match-string 2))
|
||||||
(expand-file-name (match-string 1) default-directory))
|
(expand-file-name (match-string 1) default-directory))
|
||||||
))))
|
))))
|
||||||
|
|
|
@ -839,16 +839,16 @@ and just use etags."
|
||||||
(kill-local-variable 'xref-backend-functions))
|
(kill-local-variable 'xref-backend-functions))
|
||||||
(setq-local xref-backend-functions xref-etags-mode--saved)))
|
(setq-local xref-backend-functions xref-etags-mode--saved)))
|
||||||
|
|
||||||
(declare-function semantic-symref-find-references-by-name "semantic/symref")
|
(declare-function semantic-symref-instantiate "semantic/symref")
|
||||||
(declare-function semantic-find-file-noselect "semantic/fw")
|
(declare-function semantic-symref-perform-search "semantic/symref")
|
||||||
(declare-function grep-expand-template "grep")
|
(declare-function grep-expand-template "grep")
|
||||||
(defvar ede-minor-mode) ;; ede.el
|
(defvar ede-minor-mode) ;; ede.el
|
||||||
|
|
||||||
(defun xref-collect-references (symbol dir)
|
(defun xref-collect-references (symbol dir)
|
||||||
"Collect references to SYMBOL inside DIR.
|
"Collect references to SYMBOL inside DIR.
|
||||||
This function uses the Semantic Symbol Reference API, see
|
This function uses the Semantic Symbol Reference API, see
|
||||||
`semantic-symref-find-references-by-name' for details on which
|
`semantic-symref-tool-alist' for details on which tools are used,
|
||||||
tools are used, and when."
|
and when."
|
||||||
(cl-assert (directory-name-p dir))
|
(cl-assert (directory-name-p dir))
|
||||||
(require 'semantic/symref)
|
(require 'semantic/symref)
|
||||||
(defvar semantic-symref-tool)
|
(defvar semantic-symref-tool)
|
||||||
|
@ -859,19 +859,19 @@ tools are used, and when."
|
||||||
;; to force the backend to use `default-directory'.
|
;; to force the backend to use `default-directory'.
|
||||||
(let* ((ede-minor-mode nil)
|
(let* ((ede-minor-mode nil)
|
||||||
(default-directory dir)
|
(default-directory dir)
|
||||||
|
;; FIXME: Remove CScope and Global from the recognized tools?
|
||||||
|
;; The current implementations interpret the symbol search as
|
||||||
|
;; "find all calls to the given function", but not function
|
||||||
|
;; definition. And they return nothing when passed a variable
|
||||||
|
;; name, even a global one.
|
||||||
(semantic-symref-tool 'detect)
|
(semantic-symref-tool 'detect)
|
||||||
(case-fold-search nil)
|
(case-fold-search nil)
|
||||||
(res (semantic-symref-find-references-by-name symbol 'subdirs))
|
(inst (semantic-symref-instantiate :searchfor symbol
|
||||||
(hits (and res (oref res hit-lines)))
|
:searchtype 'symbol
|
||||||
(orig-buffers (buffer-list)))
|
:searchscope 'subdirs
|
||||||
(unwind-protect
|
:resulttype 'line-and-text)))
|
||||||
(cl-mapcan (lambda (hit) (xref--collect-matches
|
(xref--convert-hits (semantic-symref-perform-search inst)
|
||||||
hit (format "\\_<%s\\_>" (regexp-quote symbol))))
|
(format "\\_<%s\\_>" (regexp-quote symbol)))))
|
||||||
hits)
|
|
||||||
;; TODO: Implement "lightweight" buffer visiting, so that we
|
|
||||||
;; don't have to kill them.
|
|
||||||
(mapc #'kill-buffer
|
|
||||||
(cl-set-difference (buffer-list) orig-buffers)))))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun xref-collect-matches (regexp files dir ignores)
|
(defun xref-collect-matches (regexp files dir ignores)
|
||||||
|
@ -890,34 +890,19 @@ IGNORES is a list of glob patterns."
|
||||||
files
|
files
|
||||||
(expand-file-name dir)
|
(expand-file-name dir)
|
||||||
ignores))
|
ignores))
|
||||||
(orig-buffers (buffer-list))
|
|
||||||
(buf (get-buffer-create " *xref-grep*"))
|
(buf (get-buffer-create " *xref-grep*"))
|
||||||
(grep-re (caar grep-regexp-alist))
|
(grep-re (caar grep-regexp-alist))
|
||||||
(counter 0)
|
|
||||||
reporter
|
|
||||||
hits)
|
hits)
|
||||||
(with-current-buffer buf
|
(with-current-buffer buf
|
||||||
(erase-buffer)
|
(erase-buffer)
|
||||||
(call-process-shell-command command nil t)
|
(call-process-shell-command command nil t)
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(while (re-search-forward grep-re nil t)
|
(while (re-search-forward grep-re nil t)
|
||||||
(push (cons (string-to-number (match-string 2))
|
(push (list (string-to-number (match-string 2))
|
||||||
(match-string 1))
|
(match-string 1)
|
||||||
|
(buffer-substring-no-properties (point) (line-end-position)))
|
||||||
hits)))
|
hits)))
|
||||||
(setq reporter (make-progress-reporter
|
(xref--convert-hits hits regexp)))
|
||||||
(format "Collecting search results...")
|
|
||||||
0 (length hits)))
|
|
||||||
(unwind-protect
|
|
||||||
(cl-mapcan (lambda (hit)
|
|
||||||
(prog1
|
|
||||||
(progress-reporter-update reporter counter)
|
|
||||||
(cl-incf counter))
|
|
||||||
(xref--collect-matches hit regexp))
|
|
||||||
(nreverse hits))
|
|
||||||
(progress-reporter-done reporter)
|
|
||||||
;; TODO: Same as above.
|
|
||||||
(mapc #'kill-buffer
|
|
||||||
(cl-set-difference (buffer-list) orig-buffers)))))
|
|
||||||
|
|
||||||
(defun xref--rgrep-command (regexp files dir ignores)
|
(defun xref--rgrep-command (regexp files dir ignores)
|
||||||
(require 'find-dired) ; for `find-name-arg'
|
(require 'find-dired) ; for `find-name-arg'
|
||||||
|
@ -980,30 +965,71 @@ directory, used as the root of the ignore globs."
|
||||||
(match-string 1 str)))))
|
(match-string 1 str)))))
|
||||||
str t t))
|
str t t))
|
||||||
|
|
||||||
(defun xref--collect-matches (hit regexp)
|
(defvar xref--last-visiting-buffer nil)
|
||||||
(pcase-let* ((`(,line . ,file) hit)
|
(defvar xref--temp-buffer-file-name nil)
|
||||||
(buf (or (find-buffer-visiting file)
|
|
||||||
(semantic-find-file-noselect file))))
|
(defun xref--convert-hits (hits regexp)
|
||||||
(with-current-buffer buf
|
(let (xref--last-visiting-buffer
|
||||||
(save-excursion
|
(tmp-buffer (generate-new-buffer " *xref-temp*")))
|
||||||
|
(unwind-protect
|
||||||
|
(cl-mapcan (lambda (hit) (xref--collect-matches hit regexp tmp-buffer))
|
||||||
|
hits)
|
||||||
|
(kill-buffer tmp-buffer))))
|
||||||
|
|
||||||
|
(defun xref--collect-matches (hit regexp tmp-buffer)
|
||||||
|
(pcase-let* ((`(,line ,file ,text) hit)
|
||||||
|
(buf (xref--find-buffer-visiting file)))
|
||||||
|
(if buf
|
||||||
|
(with-current-buffer buf
|
||||||
|
(save-excursion
|
||||||
|
(goto-char (point-min))
|
||||||
|
(forward-line (1- line))
|
||||||
|
(xref--collect-matches-1 regexp file line
|
||||||
|
(line-beginning-position)
|
||||||
|
(line-end-position))))
|
||||||
|
;; Using the temporary buffer is both a performance and a buffer
|
||||||
|
;; management optimization.
|
||||||
|
(with-current-buffer tmp-buffer
|
||||||
|
(erase-buffer)
|
||||||
|
(unless (equal file xref--temp-buffer-file-name)
|
||||||
|
(insert-file-contents file nil 0 200)
|
||||||
|
;; Can't (setq-local delay-mode-hooks t) because of
|
||||||
|
;; bug#23272, but the performance penalty seems minimal.
|
||||||
|
(let ((buffer-file-name file)
|
||||||
|
(inhibit-message t)
|
||||||
|
message-log-max)
|
||||||
|
(ignore-errors
|
||||||
|
(set-auto-mode t)))
|
||||||
|
(setq-local xref--temp-buffer-file-name file)
|
||||||
|
(setq-local inhibit-read-only t)
|
||||||
|
(erase-buffer))
|
||||||
|
(insert text)
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(forward-line (1- line))
|
(xref--collect-matches-1 regexp file line
|
||||||
(let ((line-end (line-end-position))
|
(point)
|
||||||
(line-beg (line-beginning-position))
|
(point-max))))))
|
||||||
matches)
|
|
||||||
(syntax-propertize line-end)
|
(defun xref--collect-matches-1 (regexp file line line-beg line-end)
|
||||||
;; FIXME: This results in several lines with the same
|
(let (matches)
|
||||||
;; summary. Solve with composite pattern?
|
(syntax-propertize line-end)
|
||||||
(while (re-search-forward regexp line-end t)
|
;; FIXME: This results in several lines with the same
|
||||||
(let* ((beg-column (- (match-beginning 0) line-beg))
|
;; summary. Solve with composite pattern?
|
||||||
(end-column (- (match-end 0) line-beg))
|
(while (re-search-forward regexp line-end t)
|
||||||
(loc (xref-make-file-location file line beg-column))
|
(let* ((beg-column (- (match-beginning 0) line-beg))
|
||||||
(summary (buffer-substring line-beg line-end)))
|
(end-column (- (match-end 0) line-beg))
|
||||||
(add-face-text-property beg-column end-column 'highlight
|
(loc (xref-make-file-location file line beg-column))
|
||||||
t summary)
|
(summary (buffer-substring line-beg line-end)))
|
||||||
(push (xref-make-match summary loc (- end-column beg-column))
|
(add-face-text-property beg-column end-column 'highlight
|
||||||
matches)))
|
t summary)
|
||||||
(nreverse matches))))))
|
(push (xref-make-match summary loc (- end-column beg-column))
|
||||||
|
matches)))
|
||||||
|
(nreverse matches)))
|
||||||
|
|
||||||
|
(defun xref--find-buffer-visiting (file)
|
||||||
|
(unless (equal (car xref--last-visiting-buffer) file)
|
||||||
|
(setq xref--last-visiting-buffer
|
||||||
|
(cons file (find-buffer-visiting file))))
|
||||||
|
(cdr xref--last-visiting-buffer))
|
||||||
|
|
||||||
(provide 'xref)
|
(provide 'xref)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue