2014-12-25 22:08:19 +02:00
|
|
|
|
;; xref.el --- Cross-referencing commands -*-lexical-binding:t-*-
|
|
|
|
|
|
2015-01-01 14:26:41 -08:00
|
|
|
|
;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; This file provides a somewhat generic infrastructure for cross
|
|
|
|
|
;; referencing commands, in particular "find-definition".
|
|
|
|
|
;;
|
|
|
|
|
;; Some part of the functionality must be implemented in a language
|
|
|
|
|
;; dependent way and that's done by defining `xref-find-function',
|
|
|
|
|
;; `xref-identifier-at-point-function' and
|
|
|
|
|
;; `xref-identifier-completion-table-function', which see.
|
|
|
|
|
;;
|
|
|
|
|
;; A major mode should make these variables buffer-local first.
|
|
|
|
|
;;
|
|
|
|
|
;; `xref-find-function' can be called in several ways, see its
|
|
|
|
|
;; description. It has to operate with "xref" and "location" values.
|
|
|
|
|
;;
|
|
|
|
|
;; One would usually call `make-xref' and `xref-make-file-location',
|
|
|
|
|
;; `xref-make-buffer-location' or `xref-make-bogus-location' to create
|
2015-01-20 04:28:50 +02:00
|
|
|
|
;; them. More generally, a location must be an instance of an EIEIO
|
|
|
|
|
;; class inheriting from `xref-location' and implementing
|
|
|
|
|
;; `xref-location-group' and `xref-location-marker'.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;
|
|
|
|
|
;; Each identifier must be represented as a string. Implementers can
|
|
|
|
|
;; use string properties to store additional information about the
|
|
|
|
|
;; identifier, but they should keep in mind that values returned from
|
|
|
|
|
;; `xref-identifier-completion-table-function' should still be
|
|
|
|
|
;; distinct, because the user can't see the properties when making the
|
|
|
|
|
;; choice.
|
|
|
|
|
;;
|
|
|
|
|
;; See the functions `etags-xref-find' and `elisp-xref-find' for full
|
|
|
|
|
;; examples.
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(require 'cl-lib)
|
|
|
|
|
(require 'eieio)
|
|
|
|
|
(require 'ring)
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(require 'pcase)
|
2015-07-10 04:34:41 +03:00
|
|
|
|
(require 'project)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defgroup xref nil "Cross-referencing commands"
|
|
|
|
|
:group 'tools)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Locations
|
|
|
|
|
|
|
|
|
|
(defclass xref-location () ()
|
|
|
|
|
:documentation "A location represents a position in a file or buffer.")
|
|
|
|
|
|
|
|
|
|
;; If a backend decides to subclass xref-location it can provide
|
|
|
|
|
;; methods for some of the following functions:
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defgeneric xref-location-marker (location)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
"Return the marker for LOCATION.")
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defgeneric xref-location-group (location)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
"Return a string used to group a set of locations.
|
|
|
|
|
This is typically the filename.")
|
|
|
|
|
|
2015-05-05 02:44:20 +03:00
|
|
|
|
(cl-defgeneric xref-location-line (_location)
|
|
|
|
|
"Return the line number corresponding to the location."
|
|
|
|
|
nil)
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;;; Commonly needed location classes are defined here:
|
|
|
|
|
|
|
|
|
|
;; FIXME: might be useful to have an optional "hint" i.e. a string to
|
|
|
|
|
;; search for in case the line number is sightly out of date.
|
|
|
|
|
(defclass xref-file-location (xref-location)
|
|
|
|
|
((file :type string :initarg :file)
|
2015-05-05 02:44:20 +03:00
|
|
|
|
(line :type fixnum :initarg :line :reader xref-location-line)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(column :type fixnum :initarg :column))
|
|
|
|
|
:documentation "A file location is a file/line/column triple.
|
|
|
|
|
Line numbers start from 1 and columns from 0.")
|
|
|
|
|
|
|
|
|
|
(defun xref-make-file-location (file line column)
|
|
|
|
|
"Create and return a new xref-file-location."
|
|
|
|
|
(make-instance 'xref-file-location :file file :line line :column column))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-marker ((l xref-file-location))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(with-slots (file line column) l
|
|
|
|
|
(with-current-buffer
|
|
|
|
|
(or (get-file-buffer file)
|
|
|
|
|
(let ((find-file-suppress-same-file-warnings t))
|
|
|
|
|
(find-file-noselect file)))
|
|
|
|
|
(save-restriction
|
|
|
|
|
(widen)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(beginning-of-line line)
|
|
|
|
|
(move-to-column column)
|
|
|
|
|
(point-marker))))))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-group ((l xref-file-location))
|
2015-06-24 16:32:09 -04:00
|
|
|
|
(oref l file))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defclass xref-buffer-location (xref-location)
|
|
|
|
|
((buffer :type buffer :initarg :buffer)
|
|
|
|
|
(position :type fixnum :initarg :position)))
|
|
|
|
|
|
|
|
|
|
(defun xref-make-buffer-location (buffer position)
|
|
|
|
|
"Create and return a new xref-buffer-location."
|
|
|
|
|
(make-instance 'xref-buffer-location :buffer buffer :position position))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-marker ((l xref-buffer-location))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(with-slots (buffer position) l
|
|
|
|
|
(let ((m (make-marker)))
|
|
|
|
|
(move-marker m position buffer))))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-group ((l xref-buffer-location))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(with-slots (buffer) l
|
|
|
|
|
(or (buffer-file-name buffer)
|
|
|
|
|
(format "(buffer %s)" (buffer-name buffer)))))
|
|
|
|
|
|
|
|
|
|
(defclass xref-bogus-location (xref-location)
|
|
|
|
|
((message :type string :initarg :message
|
|
|
|
|
:reader xref-bogus-location-message))
|
|
|
|
|
:documentation "Bogus locations are sometimes useful to
|
|
|
|
|
indicate errors, e.g. when we know that a function exists but the
|
|
|
|
|
actual location is not known.")
|
|
|
|
|
|
|
|
|
|
(defun xref-make-bogus-location (message)
|
|
|
|
|
"Create and return a new xref-bogus-location."
|
|
|
|
|
(make-instance 'xref-bogus-location :message message))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-marker ((l xref-bogus-location))
|
2015-06-24 16:32:09 -04:00
|
|
|
|
(user-error "%s" (oref l message)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-group ((_ xref-bogus-location)) "(No location)")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Cross-reference
|
|
|
|
|
|
|
|
|
|
(defclass xref--xref ()
|
|
|
|
|
((description :type string :initarg :description
|
|
|
|
|
:reader xref--xref-description)
|
2015-06-01 22:45:15 +03:00
|
|
|
|
(location :initarg :location
|
2014-12-25 22:08:19 +02:00
|
|
|
|
:reader xref--xref-location))
|
|
|
|
|
:comment "An xref is used to display and locate constructs like
|
|
|
|
|
variables or functions.")
|
|
|
|
|
|
|
|
|
|
(defun xref-make (description location)
|
|
|
|
|
"Create and return a new xref.
|
|
|
|
|
DESCRIPTION is a short string to describe the xref.
|
|
|
|
|
LOCATION is an `xref-location'."
|
|
|
|
|
(make-instance 'xref--xref :description description :location location))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; API
|
|
|
|
|
|
|
|
|
|
(declare-function etags-xref-find "etags" (action id))
|
|
|
|
|
(declare-function tags-lazy-completion-table "etags" ())
|
|
|
|
|
|
|
|
|
|
;; For now, make the etags backend the default.
|
|
|
|
|
(defvar xref-find-function #'etags-xref-find
|
|
|
|
|
"Function to look for cross-references.
|
|
|
|
|
It can be called in several ways:
|
|
|
|
|
|
|
|
|
|
(definitions IDENTIFIER): Find definitions of IDENTIFIER. The
|
|
|
|
|
result must be a list of xref objects. If no definitions can be
|
|
|
|
|
found, return nil.
|
|
|
|
|
|
|
|
|
|
(references IDENTIFIER): Find references of IDENTIFIER. The
|
|
|
|
|
result must be a list of xref objects. If no references can be
|
|
|
|
|
found, return nil.
|
|
|
|
|
|
|
|
|
|
(apropos PATTERN): Find all symbols that match PATTERN. PATTERN
|
|
|
|
|
is a regexp.
|
|
|
|
|
|
|
|
|
|
IDENTIFIER can be any string returned by
|
|
|
|
|
`xref-identifier-at-point-function', or from the table returned
|
|
|
|
|
by `xref-identifier-completion-table-function'.
|
|
|
|
|
|
|
|
|
|
To create an xref object, call `xref-make'.")
|
|
|
|
|
|
|
|
|
|
(defvar xref-identifier-at-point-function #'xref-default-identifier-at-point
|
|
|
|
|
"Function to get the relevant identifier at point.
|
|
|
|
|
|
|
|
|
|
The return value must be a string or nil. nil means no
|
|
|
|
|
identifier at point found.
|
|
|
|
|
|
2014-12-25 18:18:42 -08:00
|
|
|
|
If it's hard to determine the identifier precisely (e.g., because
|
2014-12-25 22:08:19 +02:00
|
|
|
|
it's a method call on unknown type), the implementation can
|
|
|
|
|
return a simple string (such as symbol at point) marked with a
|
|
|
|
|
special text property which `xref-find-function' would recognize
|
|
|
|
|
and then delegate the work to an external process.")
|
|
|
|
|
|
|
|
|
|
(defvar xref-identifier-completion-table-function #'tags-lazy-completion-table
|
|
|
|
|
"Function that returns the completion table for identifiers.")
|
|
|
|
|
|
|
|
|
|
(defun xref-default-identifier-at-point ()
|
|
|
|
|
(let ((thing (thing-at-point 'symbol)))
|
|
|
|
|
(and thing (substring-no-properties thing))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; misc utilities
|
|
|
|
|
(defun xref--alistify (list key test)
|
|
|
|
|
"Partition the elements of LIST into an alist.
|
|
|
|
|
KEY extracts the key from an element and TEST is used to compare
|
|
|
|
|
keys."
|
|
|
|
|
(let ((alist '()))
|
|
|
|
|
(dolist (e list)
|
|
|
|
|
(let* ((k (funcall key e))
|
|
|
|
|
(probe (cl-assoc k alist :test test)))
|
|
|
|
|
(if probe
|
|
|
|
|
(setcdr probe (cons e (cdr probe)))
|
|
|
|
|
(push (cons k (list e)) alist))))
|
|
|
|
|
;; Put them back in order.
|
|
|
|
|
(cl-loop for (key . value) in (reverse alist)
|
|
|
|
|
collect (cons key (reverse value)))))
|
|
|
|
|
|
|
|
|
|
(defun xref--insert-propertized (props &rest strings)
|
|
|
|
|
"Insert STRINGS with text properties PROPS."
|
|
|
|
|
(let ((start (point)))
|
|
|
|
|
(apply #'insert strings)
|
|
|
|
|
(add-text-properties start (point) props)))
|
|
|
|
|
|
|
|
|
|
(defun xref--search-property (property &optional backward)
|
|
|
|
|
"Search the next text range where text property PROPERTY is non-nil.
|
|
|
|
|
Return the value of PROPERTY. If BACKWARD is non-nil, search
|
|
|
|
|
backward."
|
|
|
|
|
(let ((next (if backward
|
|
|
|
|
#'previous-single-char-property-change
|
|
|
|
|
#'next-single-char-property-change))
|
|
|
|
|
(start (point))
|
|
|
|
|
(value nil))
|
|
|
|
|
(while (progn
|
|
|
|
|
(goto-char (funcall next (point) property))
|
|
|
|
|
(not (or (setq value (get-text-property (point) property))
|
|
|
|
|
(eobp)
|
|
|
|
|
(bobp)))))
|
|
|
|
|
(cond (value)
|
|
|
|
|
(t (goto-char start) nil))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Marker stack (M-. pushes, M-, pops)
|
|
|
|
|
|
|
|
|
|
(defcustom xref-marker-ring-length 16
|
|
|
|
|
"Length of the xref marker ring."
|
|
|
|
|
:type 'integer
|
|
|
|
|
:version "25.1")
|
|
|
|
|
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(defcustom xref-prompt-for-identifier '(not xref-find-definitions
|
|
|
|
|
xref-find-definitions-other-window
|
|
|
|
|
xref-find-definitions-other-frame)
|
|
|
|
|
"When t, always prompt for the identifier name.
|
2015-04-26 18:08:56 +03:00
|
|
|
|
|
2015-05-26 19:28:38 +03:00
|
|
|
|
When nil, prompt only when there's no value at point we can use,
|
|
|
|
|
or when the command has been called with the prefix argument.
|
|
|
|
|
|
|
|
|
|
Otherwise, it's a list of xref commands which will prompt
|
|
|
|
|
anyway (the value at point, if any, will be used as the default).
|
|
|
|
|
|
|
|
|
|
If the list starts with `not', the meaning of the rest of the
|
|
|
|
|
elements is negated."
|
2015-04-26 18:08:56 +03:00
|
|
|
|
:type '(choice (const :tag "always" t)
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(const :tag "auto" nil)
|
|
|
|
|
(set :menu-tag "command specific" :tag "commands"
|
|
|
|
|
:value (not)
|
|
|
|
|
(const :tag "Except" not)
|
|
|
|
|
(repeat :inline t (symbol :tag "command"))))
|
2015-04-26 18:08:56 +03:00
|
|
|
|
:version "25.1")
|
|
|
|
|
|
2015-05-04 18:09:33 +03:00
|
|
|
|
(defcustom xref-pulse-on-jump t
|
|
|
|
|
"When non-nil, momentarily highlight jump locations."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:version "25.1")
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defvar xref--marker-ring (make-ring xref-marker-ring-length)
|
|
|
|
|
"Ring of markers to implement the marker stack.")
|
|
|
|
|
|
2015-04-17 12:32:33 +08:00
|
|
|
|
(defun xref-push-marker-stack (&optional m)
|
|
|
|
|
"Add point M (defaults to `point-marker') to the marker stack."
|
|
|
|
|
(ring-insert xref--marker-ring (or m (point-marker))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-pop-marker-stack ()
|
|
|
|
|
"Pop back to where \\[xref-find-definitions] was last invoked."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((ring xref--marker-ring))
|
|
|
|
|
(when (ring-empty-p ring)
|
|
|
|
|
(error "Marker stack is empty"))
|
|
|
|
|
(let ((marker (ring-remove ring 0)))
|
|
|
|
|
(switch-to-buffer (or (marker-buffer marker)
|
|
|
|
|
(error "The marked buffer has been deleted")))
|
|
|
|
|
(goto-char (marker-position marker))
|
2015-05-04 18:09:33 +03:00
|
|
|
|
(set-marker marker nil nil)
|
|
|
|
|
(xref--maybe-pulse))))
|
|
|
|
|
|
|
|
|
|
(defun xref--maybe-pulse ()
|
|
|
|
|
(when xref-pulse-on-jump
|
2015-05-04 23:16:12 +03:00
|
|
|
|
(let (beg end)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(if (eolp)
|
|
|
|
|
(setq beg (line-beginning-position)
|
|
|
|
|
end (1+ (point)))
|
|
|
|
|
(setq beg (point)
|
|
|
|
|
end (line-end-position))))
|
|
|
|
|
(pulse-momentary-highlight-region beg end 'next-error))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;; etags.el needs this
|
|
|
|
|
(defun xref-clear-marker-stack ()
|
|
|
|
|
"Discard all markers from the marker stack."
|
|
|
|
|
(let ((ring xref--marker-ring))
|
|
|
|
|
(while (not (ring-empty-p ring))
|
|
|
|
|
(let ((marker (ring-remove ring)))
|
|
|
|
|
(set-marker marker nil nil)))))
|
|
|
|
|
|
2015-01-12 18:26:39 +01:00
|
|
|
|
;;;###autoload
|
2015-01-03 16:02:04 +02:00
|
|
|
|
(defun xref-marker-stack-empty-p ()
|
|
|
|
|
"Return t if the marker stack is empty; nil otherwise."
|
|
|
|
|
(ring-empty-p xref--marker-ring))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref--goto-location (location)
|
|
|
|
|
"Set buffer and point according to xref-location LOCATION."
|
|
|
|
|
(let ((marker (xref-location-marker location)))
|
|
|
|
|
(set-buffer (marker-buffer marker))
|
|
|
|
|
(cond ((and (<= (point-min) marker) (<= marker (point-max))))
|
|
|
|
|
(widen-automatically (widen))
|
|
|
|
|
(t (error "Location is outside accessible part of buffer")))
|
|
|
|
|
(goto-char marker)))
|
|
|
|
|
|
|
|
|
|
(defun xref--pop-to-location (location &optional window)
|
|
|
|
|
"Goto xref-location LOCATION and display the buffer.
|
|
|
|
|
WINDOW controls how the buffer is displayed:
|
|
|
|
|
nil -- switch-to-buffer
|
|
|
|
|
'window -- pop-to-buffer (other window)
|
|
|
|
|
'frame -- pop-to-buffer (other frame)"
|
|
|
|
|
(xref--goto-location location)
|
|
|
|
|
(cl-ecase window
|
|
|
|
|
((nil) (switch-to-buffer (current-buffer)))
|
|
|
|
|
(window (pop-to-buffer (current-buffer) t))
|
2015-05-04 18:09:33 +03:00
|
|
|
|
(frame (let ((pop-up-frames t)) (pop-to-buffer (current-buffer) t))))
|
|
|
|
|
(xref--maybe-pulse))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; XREF buffer (part of the UI)
|
|
|
|
|
|
|
|
|
|
;; The xref buffer is used to display a set of xrefs.
|
|
|
|
|
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(defvar-local xref--display-history nil
|
|
|
|
|
"List of pairs (BUFFER . WINDOW), for temporarily displayed buffers.")
|
2015-01-03 15:53:18 +02:00
|
|
|
|
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(defvar-local xref--temporary-buffers nil
|
|
|
|
|
"List of buffers created by xref code.")
|
|
|
|
|
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(defvar-local xref--current nil
|
|
|
|
|
"Non-nil if this buffer was once current, except while displaying xrefs.
|
2015-01-21 08:43:39 +02:00
|
|
|
|
Used for temporary buffers.")
|
|
|
|
|
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(defvar xref--inhibit-mark-current nil)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
|
|
|
|
|
(defun xref--mark-selected ()
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(unless xref--inhibit-mark-current
|
|
|
|
|
(setq xref--current t))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(remove-hook 'buffer-list-update-hook #'xref--mark-selected t))
|
|
|
|
|
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(defun xref--save-to-history (buf win)
|
|
|
|
|
(let ((restore (window-parameter win 'quit-restore)))
|
|
|
|
|
;; Save the new entry if the window displayed another buffer
|
|
|
|
|
;; previously.
|
|
|
|
|
(when (and restore (not (eq (car restore) 'same)))
|
|
|
|
|
(push (cons buf win) xref--display-history))))
|
|
|
|
|
|
|
|
|
|
(defun xref--display-position (pos other-window recenter-arg xref-buf)
|
|
|
|
|
;; Show the location, but don't hijack focus.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(with-selected-window (display-buffer (current-buffer) other-window)
|
|
|
|
|
(goto-char pos)
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(recenter recenter-arg)
|
2015-05-04 18:09:33 +03:00
|
|
|
|
(xref--maybe-pulse)
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(let ((buf (current-buffer))
|
|
|
|
|
(win (selected-window)))
|
|
|
|
|
(with-current-buffer xref-buf
|
2015-01-19 05:29:37 +02:00
|
|
|
|
(setq-local other-window-scroll-buffer buf)
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(xref--save-to-history buf win)))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref--show-location (location)
|
|
|
|
|
(condition-case err
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(let ((xref-buf (current-buffer))
|
|
|
|
|
(bl (buffer-list))
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(xref--inhibit-mark-current t))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(xref--goto-location location)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(let ((buf (current-buffer)))
|
|
|
|
|
(unless (memq buf bl)
|
|
|
|
|
;; Newly created.
|
|
|
|
|
(add-hook 'buffer-list-update-hook #'xref--mark-selected nil t)
|
|
|
|
|
(with-current-buffer xref-buf
|
|
|
|
|
(push buf xref--temporary-buffers))))
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(xref--display-position (point) t 1 xref-buf))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(user-error (message (error-message-string err)))))
|
|
|
|
|
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(defun xref-show-location-at-point ()
|
|
|
|
|
"Display the source of xref at point in the other window, if any."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((loc (xref--location-at-point)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(when loc
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(xref--show-location loc))))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defun xref-next-line ()
|
|
|
|
|
"Move to the next xref and display its source in the other window."
|
|
|
|
|
(interactive)
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(xref--search-property 'xref-location)
|
|
|
|
|
(xref-show-location-at-point))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref-prev-line ()
|
|
|
|
|
"Move to the previous xref and display its source in the other window."
|
|
|
|
|
(interactive)
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(xref--search-property 'xref-location t)
|
|
|
|
|
(xref-show-location-at-point))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref--location-at-point ()
|
2015-05-05 05:05:02 +03:00
|
|
|
|
(save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(get-text-property (point) 'xref-location)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(defvar-local xref--window nil
|
|
|
|
|
"ACTION argument to call `display-buffer' with.")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref-goto-xref ()
|
2015-01-18 10:15:41 +02:00
|
|
|
|
"Jump to the xref on the current line and bury the xref buffer."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(interactive)
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(let ((loc (or (xref--location-at-point)
|
2015-01-19 05:29:37 +02:00
|
|
|
|
(user-error "No reference at point")))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(window xref--window))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(xref-quit)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(xref--pop-to-location loc window)))
|
|
|
|
|
|
2015-01-21 09:20:04 +02:00
|
|
|
|
(defvar xref--xref-buffer-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(define-key map [remap quit-window] #'xref-quit)
|
|
|
|
|
(define-key map (kbd "n") #'xref-next-line)
|
|
|
|
|
(define-key map (kbd "p") #'xref-prev-line)
|
|
|
|
|
(define-key map (kbd "RET") #'xref-goto-xref)
|
|
|
|
|
(define-key map (kbd "C-o") #'xref-show-location-at-point)
|
|
|
|
|
;; suggested by Johan Claesson "to further reduce finger movement":
|
|
|
|
|
(define-key map (kbd ".") #'xref-next-line)
|
|
|
|
|
(define-key map (kbd ",") #'xref-prev-line)
|
|
|
|
|
map))
|
|
|
|
|
|
|
|
|
|
(define-derived-mode xref--xref-buffer-mode special-mode "XREF"
|
2014-12-25 18:18:42 -08:00
|
|
|
|
"Mode for displaying cross-references."
|
2015-04-30 03:41:34 +03:00
|
|
|
|
(setq buffer-read-only t)
|
|
|
|
|
(setq next-error-function #'xref--next-error-function)
|
|
|
|
|
(setq next-error-last-buffer (current-buffer)))
|
|
|
|
|
|
|
|
|
|
(defun xref--next-error-function (n reset?)
|
|
|
|
|
(when reset?
|
|
|
|
|
(goto-char (point-min)))
|
|
|
|
|
(let ((backward (< n 0))
|
|
|
|
|
(n (abs n))
|
|
|
|
|
(loc nil))
|
|
|
|
|
(dotimes (_ n)
|
|
|
|
|
(setq loc (xref--search-property 'xref-location backward)))
|
|
|
|
|
(cond (loc
|
|
|
|
|
(xref--pop-to-location loc))
|
|
|
|
|
(t
|
|
|
|
|
(error "No %s xref" (if backward "previous" "next"))))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(defun xref-quit (&optional kill)
|
2015-01-22 04:09:23 +02:00
|
|
|
|
"Bury temporarily displayed buffers, then quit the current window.
|
|
|
|
|
|
|
|
|
|
If KILL is non-nil, kill all buffers that were created in the
|
|
|
|
|
process of showing xrefs, and also kill the current buffer.
|
|
|
|
|
|
|
|
|
|
The buffers that the user has otherwise interacted with in the
|
|
|
|
|
meantime are preserved."
|
2015-01-21 09:20:04 +02:00
|
|
|
|
(interactive "P")
|
2015-01-19 04:19:32 +02:00
|
|
|
|
(let ((window (selected-window))
|
|
|
|
|
(history xref--display-history))
|
|
|
|
|
(setq xref--display-history nil)
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(pcase-dolist (`(,buf . ,win) history)
|
|
|
|
|
(when (and (window-live-p win)
|
|
|
|
|
(eq buf (window-buffer win)))
|
|
|
|
|
(quit-window nil win)))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(when kill
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(let ((xref--inhibit-mark-current t)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
kill-buffer-query-functions)
|
|
|
|
|
(dolist (buf xref--temporary-buffers)
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(unless (buffer-local-value 'xref--current buf)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(kill-buffer buf)))
|
|
|
|
|
(setq xref--temporary-buffers nil)))
|
|
|
|
|
(quit-window kill window)))
|
2015-01-19 04:19:32 +02:00
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defconst xref-buffer-name "*xref*"
|
|
|
|
|
"The name of the buffer to show xrefs.")
|
|
|
|
|
|
2015-01-05 02:45:30 +03:00
|
|
|
|
(defvar xref--button-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(define-key map [(control ?m)] #'xref-goto-xref)
|
|
|
|
|
(define-key map [mouse-1] #'xref-goto-xref)
|
|
|
|
|
(define-key map [mouse-2] #'xref--mouse-2)
|
|
|
|
|
map))
|
|
|
|
|
|
|
|
|
|
(defun xref--mouse-2 (event)
|
|
|
|
|
"Move point to the button and show the xref definition."
|
|
|
|
|
(interactive "e")
|
|
|
|
|
(mouse-set-point event)
|
|
|
|
|
(forward-line 0)
|
|
|
|
|
(xref--search-property 'xref-location)
|
|
|
|
|
(xref-show-location-at-point))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defun xref--insert-xrefs (xref-alist)
|
|
|
|
|
"Insert XREF-ALIST in the current-buffer.
|
|
|
|
|
XREF-ALIST is of the form ((GROUP . (XREF ...)) ...). Where
|
|
|
|
|
GROUP is a string for decoration purposes and XREF is an
|
|
|
|
|
`xref--xref' object."
|
2015-05-05 05:17:14 +03:00
|
|
|
|
(require 'compile) ; For the compilation faces.
|
2015-05-05 02:44:20 +03:00
|
|
|
|
(cl-loop for ((group . xrefs) . more1) on xref-alist
|
|
|
|
|
for max-line-width =
|
|
|
|
|
(cl-loop for xref in xrefs
|
|
|
|
|
maximize (let ((line (xref-location-line
|
2015-06-24 16:32:09 -04:00
|
|
|
|
(oref xref location))))
|
2015-05-05 02:44:20 +03:00
|
|
|
|
(length (and line (format "%d" line)))))
|
|
|
|
|
for line-format = (and max-line-width
|
|
|
|
|
(format "%%%dd: " max-line-width))
|
|
|
|
|
do
|
2015-05-04 00:39:06 +03:00
|
|
|
|
(xref--insert-propertized '(face compilation-info) group "\n")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(cl-loop for (xref . more2) on xrefs do
|
|
|
|
|
(with-slots (description location) xref
|
2015-05-05 05:05:02 +03:00
|
|
|
|
(let* ((line (xref-location-line location))
|
|
|
|
|
(prefix
|
|
|
|
|
(if line
|
|
|
|
|
(propertize (format line-format line)
|
|
|
|
|
'face 'compilation-line-number)
|
|
|
|
|
" ")))
|
|
|
|
|
(xref--insert-propertized
|
|
|
|
|
(list 'xref-location location
|
|
|
|
|
;; 'face 'font-lock-keyword-face
|
|
|
|
|
'mouse-face 'highlight
|
|
|
|
|
'keymap xref--button-map
|
|
|
|
|
'help-echo
|
|
|
|
|
(concat "mouse-2: display in another window, "
|
|
|
|
|
"RET or mouse-1: follow reference"))
|
|
|
|
|
prefix description)))
|
2015-05-04 00:39:06 +03:00
|
|
|
|
(insert "\n"))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref--analyze (xrefs)
|
|
|
|
|
"Find common filenames in XREFS.
|
|
|
|
|
Return an alist of the form ((FILENAME . (XREF ...)) ...)."
|
|
|
|
|
(xref--alistify xrefs
|
|
|
|
|
(lambda (x)
|
|
|
|
|
(xref-location-group (xref--xref-location x)))
|
|
|
|
|
#'equal))
|
|
|
|
|
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(defun xref--show-xref-buffer (xrefs alist)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(let ((xref-alist (xref--analyze xrefs)))
|
|
|
|
|
(with-current-buffer (get-buffer-create xref-buffer-name)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(xref--insert-xrefs xref-alist)
|
|
|
|
|
(xref--xref-buffer-mode)
|
|
|
|
|
(pop-to-buffer (current-buffer))
|
|
|
|
|
(goto-char (point-min))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(setq xref--window (assoc-default 'window alist))
|
|
|
|
|
(setq xref--temporary-buffers (assoc-default 'temporary-buffers alist))
|
|
|
|
|
(dolist (buf xref--temporary-buffers)
|
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(add-hook 'buffer-list-update-hook #'xref--mark-selected nil t)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(current-buffer)))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; This part of the UI seems fairly uncontroversial: it reads the
|
|
|
|
|
;; identifier and deals with the single definition case.
|
|
|
|
|
;;
|
|
|
|
|
;; The controversial multiple definitions case is handed off to
|
|
|
|
|
;; xref-show-xrefs-function.
|
|
|
|
|
|
|
|
|
|
(defvar xref-show-xrefs-function 'xref--show-xref-buffer
|
|
|
|
|
"Function to display a list of xrefs.")
|
|
|
|
|
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(defvar xref--read-identifier-history nil)
|
|
|
|
|
|
|
|
|
|
(defvar xref--read-pattern-history nil)
|
|
|
|
|
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(defun xref--show-xrefs (input kind arg window)
|
|
|
|
|
(let* ((bl (buffer-list))
|
|
|
|
|
(xrefs (funcall xref-find-function kind arg))
|
|
|
|
|
(tb (cl-set-difference (buffer-list) bl)))
|
|
|
|
|
(cond
|
|
|
|
|
((null xrefs)
|
2015-07-10 04:34:41 +03:00
|
|
|
|
(user-error "No %s found for: %s" (symbol-name kind) input))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
((not (cdr xrefs))
|
|
|
|
|
(xref-push-marker-stack)
|
|
|
|
|
(xref--pop-to-location (xref--xref-location (car xrefs)) window))
|
|
|
|
|
(t
|
|
|
|
|
(xref-push-marker-stack)
|
|
|
|
|
(funcall xref-show-xrefs-function xrefs
|
|
|
|
|
`((window . ,window)
|
|
|
|
|
(temporary-buffers . ,tb)))))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(defun xref--prompt-p (command)
|
|
|
|
|
(or (eq xref-prompt-for-identifier t)
|
|
|
|
|
(if (eq (car xref-prompt-for-identifier) 'not)
|
|
|
|
|
(not (memq command (cdr xref-prompt-for-identifier)))
|
|
|
|
|
(memq command xref-prompt-for-identifier))))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defun xref--read-identifier (prompt)
|
|
|
|
|
"Return the identifier at point or read it from the minibuffer."
|
|
|
|
|
(let ((id (funcall xref-identifier-at-point-function)))
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(cond ((or current-prefix-arg
|
|
|
|
|
(not id)
|
|
|
|
|
(xref--prompt-p this-command))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(completing-read prompt
|
|
|
|
|
(funcall xref-identifier-completion-table-function)
|
2015-05-06 04:35:46 +03:00
|
|
|
|
nil nil nil
|
2015-04-25 19:23:41 +03:00
|
|
|
|
'xref--read-identifier-history id))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(t id))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Commands
|
|
|
|
|
|
|
|
|
|
(defun xref--find-definitions (id window)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(xref--show-xrefs id 'definitions id window))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-definitions (identifier)
|
|
|
|
|
"Find the definition of the identifier at point.
|
2014-12-30 21:54:03 +02:00
|
|
|
|
With prefix argument or when there's no identifier at point,
|
|
|
|
|
prompt for it."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(interactive (list (xref--read-identifier "Find definitions of: ")))
|
|
|
|
|
(xref--find-definitions identifier nil))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-definitions-other-window (identifier)
|
|
|
|
|
"Like `xref-find-definitions' but switch to the other window."
|
|
|
|
|
(interactive (list (xref--read-identifier "Find definitions of: ")))
|
|
|
|
|
(xref--find-definitions identifier 'window))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-definitions-other-frame (identifier)
|
|
|
|
|
"Like `xref-find-definitions' but switch to the other frame."
|
|
|
|
|
(interactive (list (xref--read-identifier "Find definitions of: ")))
|
|
|
|
|
(xref--find-definitions identifier 'frame))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-references (identifier)
|
|
|
|
|
"Find references to the identifier at point.
|
|
|
|
|
With prefix argument, prompt for the identifier."
|
|
|
|
|
(interactive (list (xref--read-identifier "Find references of: ")))
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(xref--show-xrefs identifier 'references identifier nil))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-05-11 02:07:27 +03:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-regexp (regexp)
|
2015-07-10 04:34:41 +03:00
|
|
|
|
"Find all matches for REGEXP.
|
|
|
|
|
With \\[universal-argument] prefix, you can specify the directory
|
|
|
|
|
to search in."
|
2015-05-26 19:28:38 +03:00
|
|
|
|
;; FIXME: Prompt for directory.
|
2015-05-11 02:07:27 +03:00
|
|
|
|
(interactive (list (xref--read-identifier "Find regexp: ")))
|
2015-07-10 04:34:41 +03:00
|
|
|
|
(let* ((dirs (if current-prefix-arg
|
|
|
|
|
(list (read-directory-name "In directory: "))
|
|
|
|
|
(let ((proj (project-current)))
|
|
|
|
|
(project--prune-directories
|
|
|
|
|
(nconc
|
|
|
|
|
(project-directories proj)
|
|
|
|
|
(project-search-path proj))))))
|
|
|
|
|
(xref-find-function
|
|
|
|
|
(lambda (_kind regexp)
|
|
|
|
|
(cl-mapcan
|
|
|
|
|
(lambda (dir)
|
|
|
|
|
(xref-collect-matches regexp dir))
|
|
|
|
|
dirs))))
|
|
|
|
|
(xref--show-xrefs regexp 'matches regexp nil)))
|
2015-05-11 02:07:27 +03:00
|
|
|
|
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(declare-function apropos-parse-pattern "apropos" (pattern))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-apropos (pattern)
|
|
|
|
|
"Find all meaningful symbols that match PATTERN.
|
|
|
|
|
The argument has the same meaning as in `apropos'."
|
2015-05-23 12:05:47 -04:00
|
|
|
|
(interactive (list (read-string
|
2015-01-22 04:09:23 +02:00
|
|
|
|
"Search for pattern (word list or regexp): "
|
2015-05-23 12:05:47 -04:00
|
|
|
|
nil 'xref--read-pattern-history)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(require 'apropos)
|
2015-01-21 08:43:39 +02:00
|
|
|
|
(xref--show-xrefs pattern 'apropos
|
|
|
|
|
(apropos-parse-pattern
|
|
|
|
|
(if (string-equal (regexp-quote pattern) pattern)
|
|
|
|
|
;; Split into words
|
|
|
|
|
(or (split-string pattern "[ \t]+" t)
|
|
|
|
|
(user-error "No word list given"))
|
|
|
|
|
pattern))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
nil))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Key bindings
|
|
|
|
|
|
|
|
|
|
;;;###autoload (define-key esc-map "." #'xref-find-definitions)
|
|
|
|
|
;;;###autoload (define-key esc-map "," #'xref-pop-marker-stack)
|
|
|
|
|
;;;###autoload (define-key esc-map [?\C-.] #'xref-find-apropos)
|
|
|
|
|
;;;###autoload (define-key ctl-x-4-map "." #'xref-find-definitions-other-window)
|
|
|
|
|
;;;###autoload (define-key ctl-x-5-map "." #'xref-find-definitions-other-frame)
|
|
|
|
|
|
2015-02-23 04:00:01 +02:00
|
|
|
|
|
|
|
|
|
;;; Helper functions
|
|
|
|
|
|
|
|
|
|
(defvar xref-etags-mode--saved nil)
|
|
|
|
|
|
|
|
|
|
(define-minor-mode xref-etags-mode
|
|
|
|
|
"Minor mode to make xref use etags again.
|
|
|
|
|
|
|
|
|
|
Certain major modes install their own mechanisms for listing
|
|
|
|
|
identifiers and navigation. Turn this on to undo those settings
|
|
|
|
|
and just use etags."
|
|
|
|
|
:lighter ""
|
|
|
|
|
(if xref-etags-mode
|
|
|
|
|
(progn
|
|
|
|
|
(setq xref-etags-mode--saved
|
|
|
|
|
(cons xref-find-function
|
|
|
|
|
xref-identifier-completion-table-function))
|
|
|
|
|
(kill-local-variable 'xref-find-function)
|
|
|
|
|
(kill-local-variable 'xref-identifier-completion-table-function))
|
|
|
|
|
(setq-local xref-find-function (car xref-etags-mode--saved))
|
|
|
|
|
(setq-local xref-identifier-completion-table-function
|
|
|
|
|
(cdr xref-etags-mode--saved))))
|
|
|
|
|
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(declare-function semantic-symref-find-references-by-name "semantic/symref")
|
2015-05-11 02:07:27 +03:00
|
|
|
|
(declare-function semantic-symref-find-text "semantic/symref")
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(declare-function semantic-find-file-noselect "semantic/fw")
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(declare-function rgrep-default-command "grep")
|
2015-05-01 21:54:33 +03:00
|
|
|
|
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(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."
|
|
|
|
|
(cl-assert (directory-name-p dir))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(require 'semantic/symref)
|
|
|
|
|
(defvar semantic-symref-tool)
|
|
|
|
|
(let* ((default-directory dir)
|
|
|
|
|
(semantic-symref-tool 'detect)
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(res (semantic-symref-find-references-by-name symbol 'subdirs))
|
2015-06-24 16:32:09 -04:00
|
|
|
|
(hits (and res (oref res hit-lines)))
|
2015-05-02 01:03:56 +03:00
|
|
|
|
(orig-buffers (buffer-list)))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(unwind-protect
|
2015-05-02 01:03:56 +03:00
|
|
|
|
(delq nil
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(mapcar (lambda (hit) (xref--collect-match
|
|
|
|
|
hit (format "\\_<%s\\_>" (regexp-quote symbol))))
|
|
|
|
|
hits))
|
|
|
|
|
(mapc #'kill-buffer
|
|
|
|
|
(cl-set-difference (buffer-list) orig-buffers)))))
|
|
|
|
|
|
|
|
|
|
(defun xref-collect-matches (regexp dir)
|
|
|
|
|
"Collect matches for REGEXP inside DIR using rgrep."
|
|
|
|
|
(cl-assert (directory-name-p dir))
|
|
|
|
|
(require 'semantic/fw)
|
|
|
|
|
(grep-compute-defaults)
|
|
|
|
|
(defvar grep-find-template)
|
2015-07-11 18:56:42 +03:00
|
|
|
|
(defvar grep-highlight-matches)
|
2015-06-26 20:21:50 +03:00
|
|
|
|
(let* ((grep-find-template (replace-regexp-in-string "-e " "-E "
|
|
|
|
|
grep-find-template t t))
|
2015-07-11 18:56:42 +03:00
|
|
|
|
(grep-highlight-matches nil)
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(command (rgrep-default-command (xref--regexp-to-extended regexp)
|
|
|
|
|
"*.*" dir))
|
|
|
|
|
(orig-buffers (buffer-list))
|
|
|
|
|
(buf (get-buffer-create " *xref-grep*"))
|
|
|
|
|
(grep-re (caar grep-regexp-alist))
|
|
|
|
|
hits)
|
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(erase-buffer)
|
2015-07-10 04:38:16 +03:00
|
|
|
|
(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))
|
|
|
|
|
hits)))
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(unwind-protect
|
|
|
|
|
(delq nil
|
2015-07-09 05:15:50 +03:00
|
|
|
|
(mapcar (lambda (hit) (xref--collect-match hit regexp))
|
|
|
|
|
(nreverse hits)))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(mapc #'kill-buffer
|
2015-05-02 01:03:56 +03:00
|
|
|
|
(cl-set-difference (buffer-list) orig-buffers)))))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
|
2015-05-11 02:07:27 +03:00
|
|
|
|
(defun xref--regexp-to-extended (str)
|
|
|
|
|
(replace-regexp-in-string
|
|
|
|
|
;; FIXME: Add tests. Move to subr.el, make a public function.
|
|
|
|
|
;; Maybe error on Emacs-only constructs.
|
|
|
|
|
"\\(?:\\\\\\\\\\)*\\(?:\\\\[][]\\)?\\(?:\\[.+?\\]\\|\\(\\\\?[(){}|]\\)\\)"
|
|
|
|
|
(lambda (str)
|
|
|
|
|
(cond
|
|
|
|
|
((not (match-beginning 1))
|
|
|
|
|
str)
|
|
|
|
|
((eq (length (match-string 1 str)) 2)
|
|
|
|
|
(concat (substring str 0 (match-beginning 1))
|
|
|
|
|
(substring (match-string 1 str) 1 2)))
|
|
|
|
|
(t
|
|
|
|
|
(concat (substring str 0 (match-beginning 1))
|
|
|
|
|
"\\"
|
|
|
|
|
(match-string 1 str)))))
|
|
|
|
|
str t t))
|
|
|
|
|
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(defun xref--collect-match (hit regexp)
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(pcase-let* ((`(,line . ,file) hit)
|
|
|
|
|
(buf (or (find-buffer-visiting file)
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(semantic-find-file-noselect file))))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- line))
|
2015-07-09 15:28:04 +03:00
|
|
|
|
(syntax-propertize (line-end-position))
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(when (re-search-forward regexp (line-end-position) t)
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(goto-char (match-beginning 0))
|
2015-05-05 02:44:20 +03:00
|
|
|
|
(xref-make (buffer-substring
|
|
|
|
|
(line-beginning-position)
|
|
|
|
|
(line-end-position))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(xref-make-file-location file line
|
|
|
|
|
(current-column))))))))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(provide 'xref)
|
|
|
|
|
|
|
|
|
|
;;; xref.el ends here
|