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)
|
||||
))
|
||||
|
||||
(defconst semantic-symref-cscope--line-re
|
||||
"^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) ")
|
||||
|
||||
(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.
|
||||
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.
|
||||
subtxt)))
|
||||
)
|
||||
(t
|
||||
(when (re-search-forward "^\\([^ ]+\\) [^ ]+ \\([0-9]+\\) " nil t)
|
||||
((eq (oref tool :resulttype) 'line-and-text)
|
||||
(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))
|
||||
(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)
|
||||
))
|
||||
|
||||
(defconst semantic-symref-global--line-re
|
||||
"^\\([^ ]+\\) +\\([0-9]+\\) \\([^ ]+\\) ")
|
||||
|
||||
(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.
|
||||
Moves cursor to end of the match."
|
||||
|
@ -57,8 +60,13 @@ Moves cursor to end of the match."
|
|||
;; Search for files
|
||||
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
||||
(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
|
||||
(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))
|
||||
(match-string 3))
|
||||
))))
|
||||
|
|
|
@ -188,6 +188,9 @@ This shell should support pipe redirect syntax."
|
|||
;; Return the answer
|
||||
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))
|
||||
"Parse one line of grep output, and return it as a match list.
|
||||
Moves cursor to end of the match."
|
||||
|
@ -195,8 +198,13 @@ Moves cursor to end of the match."
|
|||
;; Search for files
|
||||
(when (re-search-forward "^\\([^\n]+\\)$" nil t)
|
||||
(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
|
||||
(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))
|
||||
(match-string 1))
|
||||
))))
|
||||
|
|
|
@ -49,6 +49,9 @@ See the function `cedet-idutils-search' for more details.")
|
|||
(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))
|
||||
"Parse one line of grep output, and return it as a match list.
|
||||
Moves cursor to end of the match."
|
||||
|
@ -59,8 +62,13 @@ Moves cursor to end of the match."
|
|||
((eq (oref tool :searchtype) 'tagcompletions)
|
||||
(when (re-search-forward "^\\([^ ]+\\) " nil t)
|
||||
(match-string 1)))
|
||||
(t
|
||||
(when (re-search-forward "^\\(\\(?:[a-zA-Z]:\\)?[^:\n]+\\):\\([0-9]+\\):" nil t)
|
||||
((eq (oref tool :resulttype) 'line-and-text)
|
||||
(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))
|
||||
(expand-file-name (match-string 1) default-directory))
|
||||
))))
|
||||
|
|
|
@ -839,16 +839,16 @@ and just use etags."
|
|||
(kill-local-variable 'xref-backend-functions))
|
||||
(setq-local xref-backend-functions xref-etags-mode--saved)))
|
||||
|
||||
(declare-function semantic-symref-find-references-by-name "semantic/symref")
|
||||
(declare-function semantic-find-file-noselect "semantic/fw")
|
||||
(declare-function semantic-symref-instantiate "semantic/symref")
|
||||
(declare-function semantic-symref-perform-search "semantic/symref")
|
||||
(declare-function grep-expand-template "grep")
|
||||
(defvar ede-minor-mode) ;; ede.el
|
||||
|
||||
(defun xref-collect-references (symbol dir)
|
||||
"Collect references to SYMBOL inside DIR.
|
||||
This function uses the Semantic Symbol Reference API, see
|
||||
`semantic-symref-find-references-by-name' for details on which
|
||||
tools are used, and when."
|
||||
`semantic-symref-tool-alist' for details on which tools are used,
|
||||
and when."
|
||||
(cl-assert (directory-name-p dir))
|
||||
(require 'semantic/symref)
|
||||
(defvar semantic-symref-tool)
|
||||
|
@ -859,19 +859,19 @@ tools are used, and when."
|
|||
;; to force the backend to use `default-directory'.
|
||||
(let* ((ede-minor-mode nil)
|
||||
(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)
|
||||
(case-fold-search nil)
|
||||
(res (semantic-symref-find-references-by-name symbol 'subdirs))
|
||||
(hits (and res (oref res hit-lines)))
|
||||
(orig-buffers (buffer-list)))
|
||||
(unwind-protect
|
||||
(cl-mapcan (lambda (hit) (xref--collect-matches
|
||||
hit (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)))))
|
||||
(inst (semantic-symref-instantiate :searchfor symbol
|
||||
:searchtype 'symbol
|
||||
:searchscope 'subdirs
|
||||
:resulttype 'line-and-text)))
|
||||
(xref--convert-hits (semantic-symref-perform-search inst)
|
||||
(format "\\_<%s\\_>" (regexp-quote symbol)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun xref-collect-matches (regexp files dir ignores)
|
||||
|
@ -890,34 +890,19 @@ IGNORES is a list of glob patterns."
|
|||
files
|
||||
(expand-file-name dir)
|
||||
ignores))
|
||||
(orig-buffers (buffer-list))
|
||||
(buf (get-buffer-create " *xref-grep*"))
|
||||
(grep-re (caar grep-regexp-alist))
|
||||
(counter 0)
|
||||
reporter
|
||||
hits)
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)
|
||||
(call-process-shell-command command nil t)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward grep-re nil t)
|
||||
(push (cons (string-to-number (match-string 2))
|
||||
(match-string 1))
|
||||
(push (list (string-to-number (match-string 2))
|
||||
(match-string 1)
|
||||
(buffer-substring-no-properties (point) (line-end-position)))
|
||||
hits)))
|
||||
(setq reporter (make-progress-reporter
|
||||
(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)))))
|
||||
(xref--convert-hits hits regexp)))
|
||||
|
||||
(defun xref--rgrep-command (regexp files dir ignores)
|
||||
(require 'find-dired) ; for `find-name-arg'
|
||||
|
@ -980,30 +965,71 @@ directory, used as the root of the ignore globs."
|
|||
(match-string 1 str)))))
|
||||
str t t))
|
||||
|
||||
(defun xref--collect-matches (hit regexp)
|
||||
(pcase-let* ((`(,line . ,file) hit)
|
||||
(buf (or (find-buffer-visiting file)
|
||||
(semantic-find-file-noselect file))))
|
||||
(with-current-buffer buf
|
||||
(save-excursion
|
||||
(defvar xref--last-visiting-buffer nil)
|
||||
(defvar xref--temp-buffer-file-name nil)
|
||||
|
||||
(defun xref--convert-hits (hits regexp)
|
||||
(let (xref--last-visiting-buffer
|
||||
(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))
|
||||
(forward-line (1- line))
|
||||
(let ((line-end (line-end-position))
|
||||
(line-beg (line-beginning-position))
|
||||
matches)
|
||||
(syntax-propertize line-end)
|
||||
;; FIXME: This results in several lines with the same
|
||||
;; summary. Solve with composite pattern?
|
||||
(while (re-search-forward regexp line-end t)
|
||||
(let* ((beg-column (- (match-beginning 0) line-beg))
|
||||
(end-column (- (match-end 0) line-beg))
|
||||
(loc (xref-make-file-location file line beg-column))
|
||||
(summary (buffer-substring line-beg line-end)))
|
||||
(add-face-text-property beg-column end-column 'highlight
|
||||
t summary)
|
||||
(push (xref-make-match summary loc (- end-column beg-column))
|
||||
matches)))
|
||||
(nreverse matches))))))
|
||||
(xref--collect-matches-1 regexp file line
|
||||
(point)
|
||||
(point-max))))))
|
||||
|
||||
(defun xref--collect-matches-1 (regexp file line line-beg line-end)
|
||||
(let (matches)
|
||||
(syntax-propertize line-end)
|
||||
;; FIXME: This results in several lines with the same
|
||||
;; summary. Solve with composite pattern?
|
||||
(while (re-search-forward regexp line-end t)
|
||||
(let* ((beg-column (- (match-beginning 0) line-beg))
|
||||
(end-column (- (match-end 0) line-beg))
|
||||
(loc (xref-make-file-location file line beg-column))
|
||||
(summary (buffer-substring line-beg line-end)))
|
||||
(add-face-text-property beg-column end-column 'highlight
|
||||
t summary)
|
||||
(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)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue