2020-05-15 14:49:46 -04:00
|
|
|
|
;;; xref.el --- Cross-referencing commands -*-lexical-binding:t-*-
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2020-01-01 00:19:43 +00:00
|
|
|
|
;; Copyright (C) 2014-2020 Free Software Foundation, Inc.
|
2020-12-09 23:37:53 +02:00
|
|
|
|
;; Version: 1.0.4
|
2020-08-14 10:20:04 +03:00
|
|
|
|
;; Package-Requires: ((emacs "26.3"))
|
2020-05-13 11:31:21 +01:00
|
|
|
|
|
|
|
|
|
;; This is a GNU ELPA :core package. Avoid functionality that is not
|
|
|
|
|
;; compatible with the version of Emacs recorded above.
|
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
|
2017-09-13 15:52:52 -07:00
|
|
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
2016-01-03 02:03:29 +02:00
|
|
|
|
;; NOTE: The xref API is still experimental and can change in major,
|
|
|
|
|
;; backward-incompatible ways. Everyone is encouraged to try it, and
|
2016-01-03 23:51:44 -08:00
|
|
|
|
;; report to us any problems or use cases we hadn't anticipated, by
|
2016-01-03 02:03:29 +02:00
|
|
|
|
;; sending an email to emacs-devel, or `M-x report-emacs-bug'.
|
|
|
|
|
;;
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;; 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
|
2015-11-14 02:37:01 +02:00
|
|
|
|
;; dependent way and that's done by defining an xref backend.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;
|
2015-11-14 02:37:01 +02:00
|
|
|
|
;; That consists of a constructor function, which should return a
|
|
|
|
|
;; backend value, and a set of implementations for the generic
|
|
|
|
|
;; functions:
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;
|
2015-11-14 02:37:01 +02:00
|
|
|
|
;; `xref-backend-identifier-at-point',
|
|
|
|
|
;; `xref-backend-identifier-completion-table',
|
|
|
|
|
;; `xref-backend-definitions', `xref-backend-references',
|
|
|
|
|
;; `xref-backend-apropos', which see.
|
|
|
|
|
;;
|
|
|
|
|
;; A major mode would normally use `add-hook' to add the backend
|
|
|
|
|
;; constructor to `xref-backend-functions'.
|
|
|
|
|
;;
|
|
|
|
|
;; The last three methods operate with "xref" and "location" values.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;
|
|
|
|
|
;; 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
|
|
|
|
;;
|
2015-11-08 05:01:05 +02:00
|
|
|
|
;; There's a special kind of xrefs we call "match xrefs", which
|
|
|
|
|
;; correspond to search results. For these values,
|
|
|
|
|
;; `xref-match-length' must be defined, and `xref-location-marker'
|
|
|
|
|
;; must return the beginning of the match.
|
|
|
|
|
;;
|
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
|
2015-11-14 02:37:01 +02:00
|
|
|
|
;; `xref-backend-identifier-completion-table' should still be
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;; distinct, because the user can't see the properties when making the
|
|
|
|
|
;; choice.
|
|
|
|
|
;;
|
2015-11-14 02:37:01 +02:00
|
|
|
|
;; See the etags and elisp-mode implementations for full examples.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(require 'cl-lib)
|
|
|
|
|
(require 'eieio)
|
|
|
|
|
(require 'ring)
|
2015-07-10 04:34:41 +03:00
|
|
|
|
(require 'project)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defgroup xref nil "Cross-referencing commands"
|
2016-01-12 20:06:49 -05:00
|
|
|
|
:version "25.1"
|
2014-12-25 22:08:19 +02:00
|
|
|
|
:group 'tools)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Locations
|
|
|
|
|
|
|
|
|
|
(defclass xref-location () ()
|
|
|
|
|
:documentation "A location represents a position in a file or buffer.")
|
|
|
|
|
|
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)
|
|
|
|
|
|
2020-12-21 03:38:37 +02:00
|
|
|
|
(cl-defgeneric xref-location-column (_location)
|
|
|
|
|
"Return the exact column corresponding to the location."
|
|
|
|
|
nil)
|
|
|
|
|
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(cl-defgeneric xref-match-length (_item)
|
|
|
|
|
"Return the length of the match."
|
2015-07-20 04:32:58 +03:00
|
|
|
|
nil)
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;;; Commonly needed location classes are defined here:
|
|
|
|
|
|
2019-07-17 09:17:36 -07:00
|
|
|
|
(defcustom xref-file-name-display 'abs
|
2019-07-25 19:32:25 +03:00
|
|
|
|
"Style of file name display in *xref* buffers.
|
|
|
|
|
If the value is the symbol `abs', the default, show the file names
|
|
|
|
|
in their full absolute form.
|
|
|
|
|
If `nondirectory', show only the nondirectory (a.k.a. \"base name\")
|
|
|
|
|
part of the file name."
|
2019-07-17 09:17:36 -07:00
|
|
|
|
:type '(choice (const :tag "absolute file name" abs)
|
|
|
|
|
(const :tag "nondirectory file name" nondirectory))
|
|
|
|
|
:version "27.1")
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;; FIXME: might be useful to have an optional "hint" i.e. a string to
|
2017-10-31 22:01:34 +01:00
|
|
|
|
;; search for in case the line number is slightly out of date.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(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)
|
2020-12-21 03:38:37 +02:00
|
|
|
|
(column :type fixnum :initarg :column :reader xref-location-column))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
: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)
|
2015-11-04 10:07:25 +01:00
|
|
|
|
"Create and return a new `xref-file-location'."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(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))
|
2019-10-20 06:39:02 -07:00
|
|
|
|
(ignore-errors
|
|
|
|
|
;; xref location may be out of date; it may be past the
|
|
|
|
|
;; end of the current file, or the file may have been
|
|
|
|
|
;; deleted. Return a reasonable location; the user will
|
|
|
|
|
;; figure it out.
|
|
|
|
|
(beginning-of-line line)
|
|
|
|
|
(forward-char column))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(point-marker))))))
|
|
|
|
|
|
2015-01-22 04:24:31 +02:00
|
|
|
|
(cl-defmethod xref-location-group ((l xref-file-location))
|
2019-07-17 09:17:36 -07:00
|
|
|
|
(cl-ecase xref-file-name-display
|
|
|
|
|
(abs (oref l file))
|
|
|
|
|
(nondirectory (file-name-nondirectory (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)
|
2015-11-04 10:07:25 +01:00
|
|
|
|
"Create and return a new `xref-buffer-location'."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(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)
|
2015-11-04 10:07:25 +01:00
|
|
|
|
"Create and return a new `xref-bogus-location'."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(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
|
|
|
|
|
|
2015-07-20 03:26:01 +03:00
|
|
|
|
(defclass xref-item ()
|
2015-07-20 03:12:32 +03:00
|
|
|
|
((summary :type string :initarg :summary
|
2015-07-20 03:26:01 +03:00
|
|
|
|
:reader xref-item-summary
|
|
|
|
|
:documentation "One line which will be displayed for
|
|
|
|
|
this item in the output buffer.")
|
2015-06-01 22:45:15 +03:00
|
|
|
|
(location :initarg :location
|
2015-07-20 03:26:01 +03:00
|
|
|
|
:reader xref-item-location
|
|
|
|
|
:documentation "An object describing how to navigate
|
|
|
|
|
to the reference's target."))
|
|
|
|
|
:comment "An xref item describes a reference to a location
|
|
|
|
|
somewhere.")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-07-20 03:12:32 +03:00
|
|
|
|
(defun xref-make (summary location)
|
2015-11-04 10:07:25 +01:00
|
|
|
|
"Create and return a new `xref-item'.
|
2015-07-20 03:12:32 +03:00
|
|
|
|
SUMMARY is a short string to describe the xref.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
LOCATION is an `xref-location'."
|
2015-07-20 03:26:01 +03:00
|
|
|
|
(make-instance 'xref-item :summary summary :location location))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(defclass xref-match-item ()
|
|
|
|
|
((summary :type string :initarg :summary
|
|
|
|
|
:reader xref-item-summary)
|
|
|
|
|
(location :initarg :location
|
|
|
|
|
:type xref-file-location
|
|
|
|
|
:reader xref-item-location)
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(length :initarg :length :reader xref-match-length))
|
|
|
|
|
:comment "A match xref item describes a search result.")
|
2015-07-20 04:32:58 +03:00
|
|
|
|
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(defun xref-make-match (summary location length)
|
2015-11-04 10:07:25 +01:00
|
|
|
|
"Create and return a new `xref-match-item'.
|
2015-07-20 04:32:58 +03:00
|
|
|
|
SUMMARY is a short string to describe the xref.
|
2015-11-08 05:01:05 +02:00
|
|
|
|
LOCATION is an `xref-location'.
|
|
|
|
|
LENGTH is the match length, in characters."
|
|
|
|
|
(make-instance 'xref-match-item :summary summary
|
|
|
|
|
:location location :length length))
|
2015-07-20 04:32:58 +03:00
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;; API
|
|
|
|
|
|
2015-11-21 01:57:00 +02:00
|
|
|
|
(defvar xref-backend-functions nil
|
2015-11-14 02:37:01 +02:00
|
|
|
|
"Special hook to find the xref backend for the current context.
|
2016-01-09 19:06:52 +02:00
|
|
|
|
Each function on this hook is called in turn with no arguments,
|
2015-11-14 02:37:01 +02:00
|
|
|
|
and should return either nil to mean that it is not applicable,
|
|
|
|
|
or an xref backend, which is a value to be used to dispatch the
|
|
|
|
|
generic functions.")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-11-21 01:57:00 +02:00
|
|
|
|
;; We make the etags backend the default for now, until something
|
2015-12-02 04:12:03 +02:00
|
|
|
|
;; better comes along. Use APPEND so that any `add-hook' calls made
|
|
|
|
|
;; before this package is loaded put new items before this one.
|
|
|
|
|
(add-hook 'xref-backend-functions #'etags--xref-backend t)
|
2015-11-21 01:57:00 +02:00
|
|
|
|
|
2015-11-15 06:31:51 +02:00
|
|
|
|
;;;###autoload
|
2015-11-14 02:37:01 +02:00
|
|
|
|
(defun xref-find-backend ()
|
|
|
|
|
(run-hook-with-args-until-success 'xref-backend-functions))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-11-14 02:37:01 +02:00
|
|
|
|
(cl-defgeneric xref-backend-definitions (backend identifier)
|
|
|
|
|
"Find definitions of IDENTIFIER.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-11-14 02:37:01 +02:00
|
|
|
|
The result must be a list of xref objects. If IDENTIFIER
|
|
|
|
|
contains sufficient information to determine a unique definition,
|
|
|
|
|
return only that definition. If there are multiple possible
|
|
|
|
|
definitions, return all of them. If no definitions can be found,
|
|
|
|
|
return nil.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
IDENTIFIER can be any string returned by
|
2015-11-14 02:37:01 +02:00
|
|
|
|
`xref-backend-identifier-at-point', or from the table returned by
|
|
|
|
|
`xref-backend-identifier-completion-table'.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
To create an xref object, call `xref-make'.")
|
|
|
|
|
|
2015-11-15 07:00:45 +02:00
|
|
|
|
(cl-defgeneric xref-backend-references (_backend identifier)
|
2015-11-14 02:37:01 +02:00
|
|
|
|
"Find references of IDENTIFIER.
|
|
|
|
|
The result must be a list of xref objects. If no references can
|
2015-11-15 07:00:45 +02:00
|
|
|
|
be found, return nil.
|
|
|
|
|
|
2015-11-17 07:46:11 -06:00
|
|
|
|
The default implementation uses `semantic-symref-tool-alist' to
|
|
|
|
|
find a search tool; by default, this uses \"find | grep\" in the
|
2020-08-14 10:20:04 +03:00
|
|
|
|
current project's main and external roots."
|
2020-06-21 14:31:16 +01:00
|
|
|
|
(mapcan
|
2015-11-15 07:00:45 +02:00
|
|
|
|
(lambda (dir)
|
2019-12-29 15:44:08 +03:00
|
|
|
|
(xref-references-in-directory identifier dir))
|
2015-11-15 07:00:45 +02:00
|
|
|
|
(let ((pr (project-current t)))
|
2020-05-23 04:38:27 +03:00
|
|
|
|
(cons
|
2020-08-14 10:20:04 +03:00
|
|
|
|
(if (fboundp 'project-root)
|
|
|
|
|
(project-root pr)
|
|
|
|
|
(with-no-warnings
|
|
|
|
|
(project-roots pr)))
|
2015-12-28 06:17:19 +02:00
|
|
|
|
(project-external-roots pr)))))
|
2015-11-14 02:37:01 +02:00
|
|
|
|
|
|
|
|
|
(cl-defgeneric xref-backend-apropos (backend pattern)
|
2020-06-01 04:44:33 +03:00
|
|
|
|
"Find all symbols that match PATTERN string.
|
|
|
|
|
The second argument has the same meaning as in `apropos'.
|
|
|
|
|
|
|
|
|
|
If BACKEND is implemented in Lisp, it can use
|
|
|
|
|
`xref-apropos-regexp' to convert the pattern to regexp.")
|
2015-11-14 02:37:01 +02:00
|
|
|
|
|
|
|
|
|
(cl-defgeneric xref-backend-identifier-at-point (_backend)
|
|
|
|
|
"Return the relevant identifier at point.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
lisp/*.el: Fix typos and improve some docstrings
* lisp/auth-source.el (auth-source-backend-parse-parameters)
(auth-source-search-collection)
(auth-source-secrets-listify-pattern)
(auth-source--decode-octal-string, auth-source-plstore-search):
* lisp/registry.el (registry-lookup)
(registry-lookup-breaks-before-lexbind)
(registry-lookup-secondary, registry-lookup-secondary-value)
(registry-search, registry-delete, registry-size, registry-full)
(registry-insert, registry-reindex, registry-prune)
(registry-collect-prune-candidates):
* lisp/subr.el (nbutlast, process-live-p):
* lisp/tab-bar.el (tab-bar-list):
* lisp/cedet/ede/linux.el (ede-linux--get-archs)
(ede-linux--include-path, ede-linux-load):
* lisp/erc/erc-log.el (erc-log-all-but-server-buffers):
* lisp/erc/erc-pcomplete.el (pcomplete-erc-commands)
(pcomplete-erc-ops, pcomplete-erc-not-ops, pcomplete-erc-nicks)
(pcomplete-erc-all-nicks, pcomplete-erc-channels)
(pcomplete-erc-command-name, pcomplete-erc-parse-arguments):
* lisp/eshell/em-term.el (eshell-visual-command-p):
* lisp/gnus/gnus-cache.el (gnus-cache-fully-p):
* lisp/gnus/nnmail.el (nnmail-get-active)
(nnmail-fancy-expiry-target):
* lisp/mail/mail-utils.el (mail-string-delete):
* lisp/mail/supercite.el (sc-hdr, sc-valid-index-p):
* lisp/net/ange-ftp.el (ange-ftp-use-smart-gateway-p):
* lisp/net/nsm.el (nsm-save-fingerprint-maybe)
(nsm-network-same-subnet, nsm-should-check):
* lisp/net/rcirc.el (rcirc-looking-at-input):
* lisp/net/tramp-cache.el (tramp-get-hash-table):
* lisp/net/tramp-compat.el (tramp-compat-process-running-p):
* lisp/net/tramp-smb.el (tramp-smb-get-share)
(tramp-smb-get-localname, tramp-smb-read-file-entry)
(tramp-smb-get-cifs-capabilities, tramp-smb-get-stat-capability):
* lisp/net/zeroconf.el (zeroconf-list-service-names)
(zeroconf-list-service-types, zeroconf-list-services)
(zeroconf-get-host, zeroconf-get-domain)
(zeroconf-get-host-domain):
* lisp/nxml/rng-xsd.el (rng-xsd-compile)
(rng-xsd-make-date-time-regexp, rng-xsd-convert-date-time):
* lisp/obsolete/erc-hecomplete.el (erc-hecomplete)
(erc-command-list, erc-complete-at-prompt):
* lisp/org/ob-scheme.el (org-babel-scheme-get-buffer-impl):
* lisp/org/ob-shell.el (org-babel--variable-assignments:sh-generic)
(org-babel--variable-assignments:bash_array)
(org-babel--variable-assignments:bash_assoc)
(org-babel--variable-assignments:bash):
* lisp/org/org-clock.el (org-day-of-week):
* lisp/progmodes/cperl-mode.el (cperl-char-ends-sub-keyword-p):
* lisp/progmodes/gud.el (gud-find-c-expr, gud-innermost-expr)
(gud-prev-expr, gud-next-expr):
* lisp/textmodes/table.el (table--at-cell-p, table--probe-cell)
(table--get-cell-justify-property)
(table--get-cell-valign-property)
(table--put-cell-justify-property)
(table--put-cell-valign-property): Fix typos.
* lisp/so-long.el (fboundp): Doc fix.
(so-long-mode-line-info, so-long-mode)
(so-long--check-header-modes): Fix typos.
* lisp/emulation/viper-mous.el (viper-surrounding-word)
(viper-mouse-click-get-word): Fix typos.
(viper-mouse-click-search-word): Doc fix.
* lisp/erc/erc-backend.el (erc-forward-word, erc-word-at-arg-p)
(erc-bounds-of-word-at-point): Fix typos.
(erc-decode-string-from-target, define-erc-response-handler):
Refill docstring.
* lisp/erc/erc-dcc.el (pcomplete/erc-mode/DCC): Fix typo.
(erc-dcc-get-host, erc-dcc-auto-mask-p, erc-dcc-get-file):
Doc fixes.
* lisp/erc/erc-networks.el (erc-network-name): Fix typo.
(erc-determine-network): Refill docstring.
* lisp/net/dbus.el (dbus-list-hash-table)
(dbus-string-to-byte-array, dbus-byte-array-to-string)
(dbus-check-event): Fix typos.
(dbus-introspect-get-property): Doc fix.
* lisp/net/tramp-adb.el (tramp-adb-file-name-handler):
Rename ARGS to ARGUMENTS. Doc fix.
(tramp-adb-sh-fix-ls-output, tramp-adb-execute-adb-command)
(tramp-adb-find-test-command): Fix typos.
* lisp/net/tramp.el (tramp-set-completion-function)
(tramp-get-completion-function)
(tramp-completion-dissect-file-name)
(tramp-completion-dissect-file-name1)
(tramp-get-completion-methods, tramp-get-completion-user-host)
(tramp-get-inode, tramp-get-device, tramp-mode-string-to-int)
(tramp-call-process, tramp-call-process-region)
(tramp-process-lines): Fix typos.
(tramp-interrupt-process): Doc fix.
* lisp/org/ob-core.el (org-babel-named-src-block-regexp-for-name)
(org-babel-named-data-regexp-for-name): Doc fix.
(org-babel-src-block-names, org-babel-result-names): Fix typos.
* lisp/progmodes/inf-lisp.el (lisp-input-filter): Doc fix.
(lisp-fn-called-at-pt): Fix typo.
* lisp/progmodes/xref.el (xref-backend-identifier-at-point):
Doc fix.
(xref-backend-identifier-completion-table): Fix typo.
2019-10-20 12:12:27 +02:00
|
|
|
|
The return value must be a string, or nil meaning no identifier
|
|
|
|
|
at point found.
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
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
|
2015-11-14 02:37:01 +02:00
|
|
|
|
special text property which e.g. `xref-backend-definitions' would
|
|
|
|
|
recognize and then delegate the work to an external process."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(let ((thing (thing-at-point 'symbol)))
|
|
|
|
|
(and thing (substring-no-properties thing))))
|
|
|
|
|
|
2015-11-14 02:37:01 +02:00
|
|
|
|
(cl-defgeneric xref-backend-identifier-completion-table (backend)
|
lisp/*.el: Fix typos and improve some docstrings
* lisp/auth-source.el (auth-source-backend-parse-parameters)
(auth-source-search-collection)
(auth-source-secrets-listify-pattern)
(auth-source--decode-octal-string, auth-source-plstore-search):
* lisp/registry.el (registry-lookup)
(registry-lookup-breaks-before-lexbind)
(registry-lookup-secondary, registry-lookup-secondary-value)
(registry-search, registry-delete, registry-size, registry-full)
(registry-insert, registry-reindex, registry-prune)
(registry-collect-prune-candidates):
* lisp/subr.el (nbutlast, process-live-p):
* lisp/tab-bar.el (tab-bar-list):
* lisp/cedet/ede/linux.el (ede-linux--get-archs)
(ede-linux--include-path, ede-linux-load):
* lisp/erc/erc-log.el (erc-log-all-but-server-buffers):
* lisp/erc/erc-pcomplete.el (pcomplete-erc-commands)
(pcomplete-erc-ops, pcomplete-erc-not-ops, pcomplete-erc-nicks)
(pcomplete-erc-all-nicks, pcomplete-erc-channels)
(pcomplete-erc-command-name, pcomplete-erc-parse-arguments):
* lisp/eshell/em-term.el (eshell-visual-command-p):
* lisp/gnus/gnus-cache.el (gnus-cache-fully-p):
* lisp/gnus/nnmail.el (nnmail-get-active)
(nnmail-fancy-expiry-target):
* lisp/mail/mail-utils.el (mail-string-delete):
* lisp/mail/supercite.el (sc-hdr, sc-valid-index-p):
* lisp/net/ange-ftp.el (ange-ftp-use-smart-gateway-p):
* lisp/net/nsm.el (nsm-save-fingerprint-maybe)
(nsm-network-same-subnet, nsm-should-check):
* lisp/net/rcirc.el (rcirc-looking-at-input):
* lisp/net/tramp-cache.el (tramp-get-hash-table):
* lisp/net/tramp-compat.el (tramp-compat-process-running-p):
* lisp/net/tramp-smb.el (tramp-smb-get-share)
(tramp-smb-get-localname, tramp-smb-read-file-entry)
(tramp-smb-get-cifs-capabilities, tramp-smb-get-stat-capability):
* lisp/net/zeroconf.el (zeroconf-list-service-names)
(zeroconf-list-service-types, zeroconf-list-services)
(zeroconf-get-host, zeroconf-get-domain)
(zeroconf-get-host-domain):
* lisp/nxml/rng-xsd.el (rng-xsd-compile)
(rng-xsd-make-date-time-regexp, rng-xsd-convert-date-time):
* lisp/obsolete/erc-hecomplete.el (erc-hecomplete)
(erc-command-list, erc-complete-at-prompt):
* lisp/org/ob-scheme.el (org-babel-scheme-get-buffer-impl):
* lisp/org/ob-shell.el (org-babel--variable-assignments:sh-generic)
(org-babel--variable-assignments:bash_array)
(org-babel--variable-assignments:bash_assoc)
(org-babel--variable-assignments:bash):
* lisp/org/org-clock.el (org-day-of-week):
* lisp/progmodes/cperl-mode.el (cperl-char-ends-sub-keyword-p):
* lisp/progmodes/gud.el (gud-find-c-expr, gud-innermost-expr)
(gud-prev-expr, gud-next-expr):
* lisp/textmodes/table.el (table--at-cell-p, table--probe-cell)
(table--get-cell-justify-property)
(table--get-cell-valign-property)
(table--put-cell-justify-property)
(table--put-cell-valign-property): Fix typos.
* lisp/so-long.el (fboundp): Doc fix.
(so-long-mode-line-info, so-long-mode)
(so-long--check-header-modes): Fix typos.
* lisp/emulation/viper-mous.el (viper-surrounding-word)
(viper-mouse-click-get-word): Fix typos.
(viper-mouse-click-search-word): Doc fix.
* lisp/erc/erc-backend.el (erc-forward-word, erc-word-at-arg-p)
(erc-bounds-of-word-at-point): Fix typos.
(erc-decode-string-from-target, define-erc-response-handler):
Refill docstring.
* lisp/erc/erc-dcc.el (pcomplete/erc-mode/DCC): Fix typo.
(erc-dcc-get-host, erc-dcc-auto-mask-p, erc-dcc-get-file):
Doc fixes.
* lisp/erc/erc-networks.el (erc-network-name): Fix typo.
(erc-determine-network): Refill docstring.
* lisp/net/dbus.el (dbus-list-hash-table)
(dbus-string-to-byte-array, dbus-byte-array-to-string)
(dbus-check-event): Fix typos.
(dbus-introspect-get-property): Doc fix.
* lisp/net/tramp-adb.el (tramp-adb-file-name-handler):
Rename ARGS to ARGUMENTS. Doc fix.
(tramp-adb-sh-fix-ls-output, tramp-adb-execute-adb-command)
(tramp-adb-find-test-command): Fix typos.
* lisp/net/tramp.el (tramp-set-completion-function)
(tramp-get-completion-function)
(tramp-completion-dissect-file-name)
(tramp-completion-dissect-file-name1)
(tramp-get-completion-methods, tramp-get-completion-user-host)
(tramp-get-inode, tramp-get-device, tramp-mode-string-to-int)
(tramp-call-process, tramp-call-process-region)
(tramp-process-lines): Fix typos.
(tramp-interrupt-process): Doc fix.
* lisp/org/ob-core.el (org-babel-named-src-block-regexp-for-name)
(org-babel-named-data-regexp-for-name): Doc fix.
(org-babel-src-block-names, org-babel-result-names): Fix typos.
* lisp/progmodes/inf-lisp.el (lisp-input-filter): Doc fix.
(lisp-fn-called-at-pt): Fix typo.
* lisp/progmodes/xref.el (xref-backend-identifier-at-point):
Doc fix.
(xref-backend-identifier-completion-table): Fix typo.
2019-10-20 12:12:27 +02:00
|
|
|
|
"Return the completion table for identifiers.")
|
2015-11-14 02:37:01 +02:00
|
|
|
|
|
2020-01-18 00:14:24 +03:00
|
|
|
|
(cl-defgeneric xref-backend-identifier-completion-ignore-case (_backend)
|
|
|
|
|
"Return t if case is not significant in identifier completion."
|
|
|
|
|
completion-ignore-case)
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
;;; 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
|
2018-10-24 20:48:15 -06:00
|
|
|
|
"Length of the xref marker ring.
|
|
|
|
|
If this variable is not set through Customize, you must call
|
|
|
|
|
`xref-set-marker-ring-length' for changes to take effect."
|
|
|
|
|
:type 'integer
|
|
|
|
|
:initialize #'custom-initialize-default
|
|
|
|
|
:set #'xref-set-marker-ring-length)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
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)
|
2018-09-19 10:54:41 +03:00
|
|
|
|
"If non-nil, prompt for the identifier to find.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2018-09-19 10:54:41 +03:00
|
|
|
|
Otherwise, it's a list of xref commands which will always prompt,
|
|
|
|
|
with the identifier at point, if any, used as the default.
|
2015-05-26 19:28:38 +03:00
|
|
|
|
If the list starts with `not', the meaning of the rest of the
|
2018-09-19 10:54:41 +03:00
|
|
|
|
elements is negated: these commands will NOT prompt."
|
|
|
|
|
:type '(choice (const :tag "Always prompt for identifier" t)
|
|
|
|
|
(const :tag "Prompt if no identifier at point" nil)
|
|
|
|
|
(set :menu-tag "Prompt according to command"
|
|
|
|
|
:tag "Prompt according to command"
|
2015-05-26 19:28:38 +03:00
|
|
|
|
:value (not)
|
2018-09-19 10:54:41 +03:00
|
|
|
|
(const :tag "Except for commands listed below" not)
|
2015-07-19 20:40:18 +03:00
|
|
|
|
(repeat :inline t (symbol :tag "command")))))
|
2015-04-26 18:08:56 +03:00
|
|
|
|
|
2015-07-19 20:40:18 +03:00
|
|
|
|
(defcustom xref-after-jump-hook '(recenter
|
|
|
|
|
xref-pulse-momentarily)
|
|
|
|
|
"Functions called after jumping to an xref."
|
|
|
|
|
:type 'hook)
|
|
|
|
|
|
|
|
|
|
(defcustom xref-after-return-hook '(xref-pulse-momentarily)
|
|
|
|
|
"Functions called after returning to a pre-jump location."
|
|
|
|
|
:type 'hook)
|
2015-05-04 18:09:33 +03:00
|
|
|
|
|
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.")
|
|
|
|
|
|
2018-10-24 20:48:15 -06:00
|
|
|
|
(defun xref-set-marker-ring-length (var val)
|
|
|
|
|
"Set `xref-marker-ring-length'.
|
|
|
|
|
VAR is the symbol `xref-marker-ring-length' and VAL is the new
|
|
|
|
|
value."
|
|
|
|
|
(set-default var val)
|
|
|
|
|
(if (ring-p xref--marker-ring)
|
|
|
|
|
(ring-resize xref--marker-ring val)))
|
|
|
|
|
|
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)
|
2015-11-14 21:13:12 +00:00
|
|
|
|
(user-error "Marker stack is empty"))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(let ((marker (ring-remove ring 0)))
|
|
|
|
|
(switch-to-buffer (or (marker-buffer marker)
|
2015-11-14 21:13:12 +00:00
|
|
|
|
(user-error "The marked buffer has been deleted")))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(goto-char (marker-position marker))
|
2015-05-04 18:09:33 +03:00
|
|
|
|
(set-marker marker nil nil)
|
2015-07-19 20:40:18 +03:00
|
|
|
|
(run-hooks 'xref-after-return-hook))))
|
|
|
|
|
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(defvar xref--current-item nil)
|
|
|
|
|
|
2015-07-19 20:40:18 +03:00
|
|
|
|
(defun xref-pulse-momentarily ()
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(pcase-let ((`(,beg . ,end)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(or
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(let ((length (xref-match-length xref--current-item)))
|
|
|
|
|
(and length (cons (point) (+ (point) length))))
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(if (eolp)
|
|
|
|
|
(cons (line-beginning-position) (1+ (point)))
|
|
|
|
|
(cons (point) (line-end-position)))))))
|
2015-07-19 20:40:18 +03:00
|
|
|
|
(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
|
|
|
|
|
2015-08-05 15:18:25 +03:00
|
|
|
|
|
|
|
|
|
(defun xref--goto-char (pos)
|
|
|
|
|
(cond
|
|
|
|
|
((and (<= (point-min) pos) (<= pos (point-max))))
|
|
|
|
|
(widen-automatically (widen))
|
|
|
|
|
(t (user-error "Position is outside accessible part of buffer")))
|
|
|
|
|
(goto-char pos))
|
|
|
|
|
|
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))
|
2015-08-05 15:18:25 +03:00
|
|
|
|
(xref--goto-char marker)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2019-06-09 17:13:40 +03:00
|
|
|
|
(defun xref-pop-to-location (item &optional action)
|
2015-07-20 04:32:58 +03:00
|
|
|
|
"Go to the location of ITEM and display the buffer.
|
2016-02-22 00:26:24 +02:00
|
|
|
|
ACTION controls how the buffer is displayed:
|
2014-12-25 22:08:19 +02:00
|
|
|
|
nil -- switch-to-buffer
|
2015-09-03 15:31:12 -07:00
|
|
|
|
`window' -- pop-to-buffer (other window)
|
2016-02-22 00:26:24 +02:00
|
|
|
|
`frame' -- pop-to-buffer (other frame)
|
|
|
|
|
If SELECT is non-nil, select the target window."
|
2015-08-05 15:18:25 +03:00
|
|
|
|
(let* ((marker (save-excursion
|
|
|
|
|
(xref-location-marker (xref-item-location item))))
|
|
|
|
|
(buf (marker-buffer marker)))
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(cl-ecase action
|
2015-08-05 15:18:25 +03:00
|
|
|
|
((nil) (switch-to-buffer buf))
|
|
|
|
|
(window (pop-to-buffer buf t))
|
|
|
|
|
(frame (let ((pop-up-frames t)) (pop-to-buffer buf t))))
|
|
|
|
|
(xref--goto-char marker))
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(let ((xref--current-item item))
|
|
|
|
|
(run-hooks 'xref-after-jump-hook)))
|
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.
|
2016-03-17 12:55:09 -04:00
|
|
|
|
(defconst xref-buffer-name "*xref*"
|
|
|
|
|
"The name of the buffer to show xrefs.")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2019-04-14 00:54:14 +03:00
|
|
|
|
(defface xref-file-header '((t :inherit compilation-info))
|
|
|
|
|
"Face used to highlight file header in the xref buffer."
|
|
|
|
|
:version "27.1")
|
|
|
|
|
|
|
|
|
|
(defface xref-line-number '((t :inherit compilation-line-number))
|
|
|
|
|
"Face for displaying line numbers in the xref buffer."
|
|
|
|
|
:version "27.1")
|
|
|
|
|
|
|
|
|
|
(defface xref-match '((t :inherit highlight))
|
|
|
|
|
"Face used to highlight matches in the xref buffer."
|
|
|
|
|
:version "27.1")
|
|
|
|
|
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(defmacro xref--with-dedicated-window (&rest body)
|
|
|
|
|
`(let* ((xref-w (get-buffer-window xref-buffer-name))
|
|
|
|
|
(xref-w-dedicated (window-dedicated-p xref-w)))
|
|
|
|
|
(unwind-protect
|
|
|
|
|
(progn
|
|
|
|
|
(when xref-w
|
|
|
|
|
(set-window-dedicated-p xref-w 'soft))
|
|
|
|
|
,@body)
|
|
|
|
|
(when xref-w
|
|
|
|
|
(set-window-dedicated-p xref-w xref-w-dedicated)))))
|
|
|
|
|
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(defvar-local xref--original-window-intent nil
|
|
|
|
|
"Original window-switching intent before xref buffer creation.")
|
|
|
|
|
|
|
|
|
|
(defvar-local xref--original-window nil
|
|
|
|
|
"The original window this xref buffer was created from.")
|
|
|
|
|
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(defvar-local xref--fetcher nil
|
|
|
|
|
"The original function to call to fetch the list of xrefs.")
|
|
|
|
|
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(defun xref--show-pos-in-buf (pos buf)
|
|
|
|
|
"Goto and display position POS of buffer BUF in a window.
|
|
|
|
|
Honor `xref--original-window-intent', run `xref-after-jump-hook'
|
|
|
|
|
and finally return the window."
|
|
|
|
|
(let* ((xref-buf (current-buffer))
|
|
|
|
|
(pop-up-frames
|
|
|
|
|
(or (eq xref--original-window-intent 'frame)
|
|
|
|
|
pop-up-frames))
|
|
|
|
|
(action
|
2019-02-04 22:52:25 +00:00
|
|
|
|
(cond ((eq xref--original-window-intent 'frame)
|
2017-10-13 15:13:14 +01:00
|
|
|
|
t)
|
2019-02-04 22:52:25 +00:00
|
|
|
|
((eq xref--original-window-intent 'window)
|
2019-06-16 03:49:46 +03:00
|
|
|
|
`((xref--display-buffer-in-other-window)
|
|
|
|
|
(window . ,xref--original-window)))
|
2017-10-13 15:13:14 +01:00
|
|
|
|
((and
|
|
|
|
|
(window-live-p xref--original-window)
|
|
|
|
|
(or (not (window-dedicated-p xref--original-window))
|
|
|
|
|
(eq (window-buffer xref--original-window) buf)))
|
2019-06-16 03:49:46 +03:00
|
|
|
|
`((xref--display-buffer-in-window)
|
|
|
|
|
(window . ,xref--original-window))))))
|
2019-02-04 22:52:25 +00:00
|
|
|
|
(with-selected-window (display-buffer buf action)
|
2015-08-10 04:30:33 +03:00
|
|
|
|
(xref--goto-char pos)
|
|
|
|
|
(run-hooks 'xref-after-jump-hook)
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(let ((buf (current-buffer)))
|
2015-08-10 04:30:33 +03:00
|
|
|
|
(with-current-buffer xref-buf
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(setq-local other-window-scroll-buffer buf)))
|
|
|
|
|
(selected-window))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2019-06-16 03:49:46 +03:00
|
|
|
|
(defun xref--display-buffer-in-other-window (buffer alist)
|
|
|
|
|
(let ((window (assoc-default 'window alist)))
|
|
|
|
|
(cl-assert window)
|
|
|
|
|
(xref--with-dedicated-window
|
|
|
|
|
(with-selected-window window
|
|
|
|
|
(display-buffer buffer t)))))
|
|
|
|
|
|
|
|
|
|
(defun xref--display-buffer-in-window (buffer alist)
|
|
|
|
|
(let ((window (assoc-default 'window alist)))
|
|
|
|
|
(cl-assert window)
|
|
|
|
|
(with-selected-window window
|
|
|
|
|
(display-buffer buffer '(display-buffer-same-window)))))
|
|
|
|
|
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(defun xref--show-location (location &optional select)
|
2017-10-13 16:37:47 +01:00
|
|
|
|
"Help `xref-show-xref' and `xref-goto-xref' do their job.
|
|
|
|
|
Go to LOCATION and if SELECT is non-nil select its window. If
|
|
|
|
|
SELECT is `quit', also quit the *xref* window."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(condition-case err
|
2015-11-03 02:36:50 +02:00
|
|
|
|
(let* ((marker (xref-location-marker location))
|
2017-10-13 16:37:47 +01:00
|
|
|
|
(buf (marker-buffer marker))
|
|
|
|
|
(xref-buffer (current-buffer)))
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(cond (select
|
2017-10-13 16:37:47 +01:00
|
|
|
|
(if (eq select 'quit) (quit-window nil nil))
|
2018-02-28 03:38:37 +02:00
|
|
|
|
(select-window
|
|
|
|
|
(with-current-buffer xref-buffer
|
|
|
|
|
(xref--show-pos-in-buf marker buf))))
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(t
|
|
|
|
|
(save-selected-window
|
|
|
|
|
(xref--with-dedicated-window
|
|
|
|
|
(xref--show-pos-in-buf marker 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 ()
|
2016-02-22 00:26:24 +02:00
|
|
|
|
"Display the source of xref at point in the appropriate window, if any."
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(interactive)
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(let* ((xref (xref--item-at-point))
|
|
|
|
|
(xref--current-item xref))
|
|
|
|
|
(when xref
|
2017-10-13 15:13:14 +01:00
|
|
|
|
(xref--show-location (xref-item-location xref)))))
|
2015-01-03 15:53:18 +02:00
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(defun xref-next-line ()
|
2016-02-22 00:26:24 +02:00
|
|
|
|
"Move to the next xref and display its source in the appropriate window."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(interactive)
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(xref--search-property 'xref-item)
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(xref-show-location-at-point))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
(defun xref-prev-line ()
|
2016-02-22 00:26:24 +02:00
|
|
|
|
"Move to the previous xref and display its source in the appropriate window."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(interactive)
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(xref--search-property 'xref-item t)
|
2015-01-03 15:53:18 +02:00
|
|
|
|
(xref-show-location-at-point))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2020-12-07 17:01:35 +01:00
|
|
|
|
(defun xref-next-group ()
|
|
|
|
|
"Move to the first item of the next xref group and display its source."
|
|
|
|
|
(interactive)
|
|
|
|
|
(xref--search-property 'xref-group)
|
|
|
|
|
(xref--search-property 'xref-item)
|
|
|
|
|
(xref-show-location-at-point))
|
|
|
|
|
|
|
|
|
|
(defun xref-prev-group ()
|
|
|
|
|
"Move to the first item of the previous xref group and display its source."
|
|
|
|
|
(interactive)
|
|
|
|
|
;; Search for the xref group of the current item, provided that the
|
|
|
|
|
;; point is not already in an xref group.
|
|
|
|
|
(unless (plist-member (text-properties-at (point)) 'xref-group)
|
|
|
|
|
(xref--search-property 'xref-group t))
|
|
|
|
|
;; Search for the previous xref group.
|
|
|
|
|
(xref--search-property 'xref-group t)
|
|
|
|
|
(xref--search-property 'xref-item)
|
|
|
|
|
(xref-show-location-at-point))
|
|
|
|
|
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(defun xref--item-at-point ()
|
2020-12-21 03:22:23 +02:00
|
|
|
|
(get-text-property
|
|
|
|
|
(if (eolp) (1- (point)) (point))
|
|
|
|
|
'xref-item))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2017-10-13 16:37:47 +01:00
|
|
|
|
(defun xref-goto-xref (&optional quit)
|
|
|
|
|
"Jump to the xref on the current line and select its window.
|
2020-11-14 22:36:13 +02:00
|
|
|
|
Non-interactively, non-nil QUIT, or interactively, with prefix argument
|
|
|
|
|
means to first quit the *xref* buffer."
|
|
|
|
|
(interactive "P")
|
2018-12-24 05:11:02 +02:00
|
|
|
|
(let* ((buffer (current-buffer))
|
|
|
|
|
(xref (or (xref--item-at-point)
|
|
|
|
|
(user-error "No reference at point")))
|
|
|
|
|
(xref--current-item xref))
|
2018-04-17 22:27:48 +03:00
|
|
|
|
(xref--show-location (xref-item-location xref) (if quit 'quit t))
|
2020-08-27 00:47:18 +03:00
|
|
|
|
(if (fboundp 'next-error-found)
|
|
|
|
|
(next-error-found buffer (current-buffer))
|
|
|
|
|
;; Emacs < 27
|
|
|
|
|
(setq next-error-last-buffer buffer))))
|
2017-10-13 16:37:47 +01:00
|
|
|
|
|
|
|
|
|
(defun xref-quit-and-goto-xref ()
|
|
|
|
|
"Quit *xref* buffer, then jump to xref on current line."
|
|
|
|
|
(interactive)
|
|
|
|
|
(xref-goto-xref t))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2016-01-24 05:17:52 +03:00
|
|
|
|
(defun xref-query-replace-in-results (from to)
|
2016-01-09 19:06:52 +02:00
|
|
|
|
"Perform interactive replacement of FROM with TO in all displayed xrefs.
|
|
|
|
|
|
|
|
|
|
This command interactively replaces FROM with TO in the names of the
|
|
|
|
|
references displayed in the current *xref* buffer."
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(interactive
|
2016-01-09 19:06:52 +02:00
|
|
|
|
(let ((fr (read-regexp "Xref query-replace (regexp)" ".*")))
|
|
|
|
|
(list fr
|
|
|
|
|
(read-regexp (format "Xref query-replace (regexp) %s with: " fr)))))
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(let* (item xrefs iter)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(while (setq item (xref--search-property 'xref-item))
|
|
|
|
|
(when (xref-match-length item)
|
|
|
|
|
(push item xrefs))))
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(unwind-protect
|
|
|
|
|
(progn
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(setq iter (xref--buf-pairs-iterator (nreverse xrefs)))
|
|
|
|
|
(xref--query-replace-1 from to iter))
|
|
|
|
|
(funcall iter :cleanup))))
|
|
|
|
|
|
|
|
|
|
(defun xref--buf-pairs-iterator (xrefs)
|
|
|
|
|
(let (chunk-done item next-pair file-buf pairs all-pairs)
|
|
|
|
|
(lambda (action)
|
|
|
|
|
(pcase action
|
|
|
|
|
(:next
|
|
|
|
|
(when (or xrefs next-pair)
|
|
|
|
|
(setq chunk-done nil)
|
|
|
|
|
(when next-pair
|
|
|
|
|
(setq file-buf (marker-buffer (car next-pair))
|
|
|
|
|
pairs (list next-pair)
|
|
|
|
|
next-pair nil))
|
|
|
|
|
(while (and (not chunk-done)
|
|
|
|
|
(setq item (pop xrefs)))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let* ((loc (xref-item-location item))
|
|
|
|
|
(beg (xref-location-marker loc))
|
|
|
|
|
(end (move-marker (make-marker)
|
|
|
|
|
(+ beg (xref-match-length item))
|
|
|
|
|
(marker-buffer beg))))
|
|
|
|
|
(let ((pair (cons beg end)))
|
|
|
|
|
(push pair all-pairs)
|
|
|
|
|
;; Perform sanity check first.
|
|
|
|
|
(xref--goto-location loc)
|
|
|
|
|
(if (xref--outdated-p item
|
|
|
|
|
(buffer-substring-no-properties
|
|
|
|
|
(line-beginning-position)
|
|
|
|
|
(line-end-position)))
|
|
|
|
|
(message "Search result out of date, skipping")
|
|
|
|
|
(cond
|
|
|
|
|
((null file-buf)
|
|
|
|
|
(setq file-buf (marker-buffer beg))
|
|
|
|
|
(push pair pairs))
|
|
|
|
|
((equal file-buf (marker-buffer beg))
|
|
|
|
|
(push pair pairs))
|
|
|
|
|
(t
|
|
|
|
|
(setq chunk-done t
|
|
|
|
|
next-pair pair))))))))
|
2016-05-05 16:01:52 +03:00
|
|
|
|
(cons file-buf (nreverse pairs))))
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(:cleanup
|
|
|
|
|
(dolist (pair all-pairs)
|
|
|
|
|
(move-marker (car pair) nil)
|
|
|
|
|
(move-marker (cdr pair) nil)))))))
|
|
|
|
|
|
|
|
|
|
(defun xref--outdated-p (item line-text)
|
|
|
|
|
;; FIXME: The check should probably be a generic function instead of
|
|
|
|
|
;; the assumption that all matches contain the full line as summary.
|
|
|
|
|
(let ((summary (xref-item-summary item))
|
|
|
|
|
(strip (lambda (s) (if (string-match "\r\\'" s)
|
|
|
|
|
(substring-no-properties s 0 -1)
|
|
|
|
|
s))))
|
|
|
|
|
(not
|
|
|
|
|
;; Sometimes buffer contents include ^M, and sometimes Grep
|
|
|
|
|
;; output includes it, and they don't always match.
|
|
|
|
|
(equal (funcall strip line-text)
|
|
|
|
|
(funcall strip summary)))))
|
2015-07-21 03:25:24 +03:00
|
|
|
|
|
2015-11-08 05:01:05 +02:00
|
|
|
|
;; FIXME: Write a nicer UI.
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(defun xref--query-replace-1 (from to iter)
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(let* ((query-replace-lazy-highlight nil)
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(continue t)
|
|
|
|
|
did-it-once buf-pairs pairs
|
|
|
|
|
current-beg current-end
|
2015-07-21 03:25:24 +03:00
|
|
|
|
;; Counteract the "do the next match now" hack in
|
|
|
|
|
;; `perform-replace'. And still, it'll report that those
|
|
|
|
|
;; matches were "filtered out" at the end.
|
|
|
|
|
(isearch-filter-predicate
|
|
|
|
|
(lambda (beg end)
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(and current-beg
|
|
|
|
|
(>= beg current-beg)
|
2015-11-15 05:59:34 +02:00
|
|
|
|
(<= end current-end))))
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(replace-re-search-function
|
|
|
|
|
(lambda (from &optional _bound noerror)
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(let (found pair)
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(while (and (not found) pairs)
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(setq pair (pop pairs)
|
|
|
|
|
current-beg (car pair)
|
2016-05-05 02:52:34 +03:00
|
|
|
|
current-end (cdr pair))
|
2015-11-08 05:01:05 +02:00
|
|
|
|
(goto-char current-beg)
|
2015-11-15 05:59:34 +02:00
|
|
|
|
(when (re-search-forward from current-end noerror)
|
2015-07-21 03:25:24 +03:00
|
|
|
|
(setq found t)))
|
|
|
|
|
found))))
|
2016-05-05 02:52:34 +03:00
|
|
|
|
(while (and continue (setq buf-pairs (funcall iter :next)))
|
|
|
|
|
(if did-it-once
|
|
|
|
|
;; Reuse the same window for subsequent buffers.
|
|
|
|
|
(switch-to-buffer (car buf-pairs))
|
|
|
|
|
(xref--with-dedicated-window
|
|
|
|
|
(pop-to-buffer (car buf-pairs)))
|
|
|
|
|
(setq did-it-once t))
|
|
|
|
|
(setq pairs (cdr buf-pairs))
|
|
|
|
|
(setq continue
|
|
|
|
|
(perform-replace from to t t nil nil multi-query-replace-map)))
|
2016-05-08 00:23:54 +03:00
|
|
|
|
(unless did-it-once (user-error "No suitable matches here"))
|
|
|
|
|
(when (and continue (not buf-pairs))
|
|
|
|
|
(message "All results processed"))))
|
2015-07-21 03:25:24 +03:00
|
|
|
|
|
2015-01-21 09:20:04 +02:00
|
|
|
|
(defvar xref--xref-buffer-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(define-key map (kbd "n") #'xref-next-line)
|
|
|
|
|
(define-key map (kbd "p") #'xref-prev-line)
|
2020-12-07 17:01:35 +01:00
|
|
|
|
(define-key map (kbd "N") #'xref-next-group)
|
|
|
|
|
(define-key map (kbd "P") #'xref-prev-group)
|
2016-01-24 05:17:52 +03:00
|
|
|
|
(define-key map (kbd "r") #'xref-query-replace-in-results)
|
2015-01-21 09:20:04 +02:00
|
|
|
|
(define-key map (kbd "RET") #'xref-goto-xref)
|
2017-10-13 16:37:47 +01:00
|
|
|
|
(define-key map (kbd "TAB") #'xref-quit-and-goto-xref)
|
2015-01-21 09:20:04 +02:00
|
|
|
|
(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)
|
2019-05-30 20:09:35 +03:00
|
|
|
|
(define-key map (kbd "g") #'xref-revert-buffer)
|
2015-01-21 09:20:04 +02:00
|
|
|
|
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)
|
2016-02-29 05:16:41 +02:00
|
|
|
|
(setq next-error-function #'xref--next-error-function)
|
2019-08-23 06:30:46 +02:00
|
|
|
|
(setq next-error-last-buffer (current-buffer))
|
|
|
|
|
(setq imenu-prev-index-position-function
|
|
|
|
|
#'xref--imenu-prev-index-position)
|
|
|
|
|
(setq imenu-extract-index-name-function
|
|
|
|
|
#'xref--imenu-extract-index-name))
|
2015-04-30 03:41:34 +03:00
|
|
|
|
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(defvar xref--transient-buffer-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(define-key map (kbd "RET") #'xref-quit-and-goto-xref)
|
|
|
|
|
(set-keymap-parent map xref--xref-buffer-mode-map)
|
|
|
|
|
map))
|
|
|
|
|
|
|
|
|
|
(define-derived-mode xref--transient-buffer-mode
|
|
|
|
|
xref--xref-buffer-mode
|
|
|
|
|
"XREF Transient")
|
|
|
|
|
|
2019-08-23 06:30:46 +02:00
|
|
|
|
(defun xref--imenu-prev-index-position ()
|
|
|
|
|
"Move point to previous line in `xref' buffer.
|
|
|
|
|
This function is used as a value for
|
|
|
|
|
`imenu-prev-index-position-function'."
|
|
|
|
|
(if (bobp)
|
|
|
|
|
nil
|
|
|
|
|
(xref--search-property 'xref-group t)))
|
|
|
|
|
|
|
|
|
|
(defun xref--imenu-extract-index-name ()
|
|
|
|
|
"Return imenu name for line at point.
|
|
|
|
|
This function is used as a value for
|
|
|
|
|
`imenu-extract-index-name-function'. Point should be at the
|
|
|
|
|
beginning of the line."
|
|
|
|
|
(buffer-substring-no-properties (line-beginning-position)
|
|
|
|
|
(line-end-position)))
|
|
|
|
|
|
2015-04-30 03:41:34 +03:00
|
|
|
|
(defun xref--next-error-function (n reset?)
|
|
|
|
|
(when reset?
|
|
|
|
|
(goto-char (point-min)))
|
|
|
|
|
(let ((backward (< n 0))
|
|
|
|
|
(n (abs n))
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(xref nil))
|
2019-02-13 21:19:36 +01:00
|
|
|
|
(if (= n 0)
|
|
|
|
|
(setq xref (get-text-property (point) 'xref-item))
|
|
|
|
|
(dotimes (_ n)
|
|
|
|
|
(setq xref (xref--search-property 'xref-item backward))))
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(cond (xref
|
2018-02-28 04:03:16 +02:00
|
|
|
|
;; Save the current position (when the buffer is visible,
|
|
|
|
|
;; it gets reset to that window's point from time to time).
|
|
|
|
|
(let ((win (get-buffer-window (current-buffer))))
|
|
|
|
|
(and win (set-window-point win (point))))
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(xref--show-location (xref-item-location xref) t))
|
2015-04-30 03:41:34 +03:00
|
|
|
|
(t
|
|
|
|
|
(error "No %s xref" (if backward "previous" "next"))))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-01-05 02:45:30 +03:00
|
|
|
|
(defvar xref--button-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(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)
|
2019-05-03 01:47:15 +03:00
|
|
|
|
(or (get-text-property (point) 'xref-item)
|
|
|
|
|
(xref--search-property 'xref-item))
|
2015-01-05 02:45:30 +03:00
|
|
|
|
(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.
|
2015-11-04 10:07:25 +01:00
|
|
|
|
XREF-ALIST is of the form ((GROUP . (XREF ...)) ...), where
|
2014-12-25 22:08:19 +02:00
|
|
|
|
GROUP is a string for decoration purposes and XREF is an
|
2015-07-20 03:26:01 +03:00
|
|
|
|
`xref-item' 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))
|
2020-12-21 03:22:23 +02:00
|
|
|
|
with prev-line-key = nil
|
2015-05-05 02:44:20 +03:00
|
|
|
|
do
|
2019-12-24 22:30:02 +02:00
|
|
|
|
(xref--insert-propertized '(face xref-file-header xref-group t)
|
2019-08-23 06:30:46 +02:00
|
|
|
|
group "\n")
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(cl-loop for (xref . more2) on xrefs do
|
2015-07-20 03:12:32 +03:00
|
|
|
|
(with-slots (summary location) xref
|
2015-05-05 05:05:02 +03:00
|
|
|
|
(let* ((line (xref-location-line location))
|
2020-12-21 03:22:23 +02:00
|
|
|
|
(new-summary summary)
|
|
|
|
|
(line-key (list (xref-location-group location) line))
|
2015-05-05 05:05:02 +03:00
|
|
|
|
(prefix
|
|
|
|
|
(if line
|
|
|
|
|
(propertize (format line-format line)
|
2019-04-14 00:54:14 +03:00
|
|
|
|
'face 'xref-line-number)
|
2015-05-05 05:05:02 +03:00
|
|
|
|
" ")))
|
2020-12-21 03:22:23 +02:00
|
|
|
|
;; Render multiple matches on the same line, together.
|
|
|
|
|
(when (and line (equal prev-line-key line-key))
|
2020-12-21 03:38:37 +02:00
|
|
|
|
(when-let ((column (xref-location-column location)))
|
2020-12-21 03:22:23 +02:00
|
|
|
|
(delete-region
|
|
|
|
|
(save-excursion
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(move-to-column (+ (length prefix) column))
|
|
|
|
|
(point))
|
|
|
|
|
(point))
|
|
|
|
|
(setq new-summary (substring summary column) prefix "")))
|
2015-05-05 05:05:02 +03:00
|
|
|
|
(xref--insert-propertized
|
2015-07-20 04:32:58 +03:00
|
|
|
|
(list 'xref-item xref
|
2015-05-05 05:05:02 +03:00
|
|
|
|
'mouse-face 'highlight
|
|
|
|
|
'keymap xref--button-map
|
|
|
|
|
'help-echo
|
|
|
|
|
(concat "mouse-2: display in another window, "
|
|
|
|
|
"RET or mouse-1: follow reference"))
|
2020-12-21 03:22:23 +02:00
|
|
|
|
prefix new-summary)
|
|
|
|
|
(setq prev-line-key line-key)))
|
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)
|
2015-07-20 03:26:01 +03:00
|
|
|
|
(xref-location-group (xref-item-location x)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
#'equal))
|
|
|
|
|
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(defun xref--show-xref-buffer (fetcher alist)
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(cl-assert (functionp fetcher))
|
|
|
|
|
(let* ((xrefs
|
|
|
|
|
(or
|
|
|
|
|
(assoc-default 'fetched-xrefs alist)
|
|
|
|
|
(funcall fetcher)))
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(xref-alist (xref--analyze xrefs)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(with-current-buffer (get-buffer-create xref-buffer-name)
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(xref--xref-buffer-mode)
|
2019-06-10 03:10:34 +03:00
|
|
|
|
(xref--show-common-initialize xref-alist fetcher alist)
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(pop-to-buffer (current-buffer))
|
|
|
|
|
(current-buffer))))
|
|
|
|
|
|
|
|
|
|
(defun xref--show-common-initialize (xref-alist fetcher alist)
|
|
|
|
|
(setq buffer-undo-list nil)
|
|
|
|
|
(let ((inhibit-read-only t)
|
|
|
|
|
(buffer-undo-list t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(xref--insert-xrefs xref-alist)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(setq xref--original-window (assoc-default 'window alist)
|
|
|
|
|
xref--original-window-intent (assoc-default 'display-action alist))
|
|
|
|
|
(setq xref--fetcher fetcher)))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2019-05-30 20:09:35 +03:00
|
|
|
|
(defun xref-revert-buffer ()
|
2019-05-25 00:15:46 +03:00
|
|
|
|
"Refresh the search results in the current buffer."
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(interactive)
|
|
|
|
|
(let ((inhibit-read-only t)
|
|
|
|
|
(buffer-undo-list t))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(condition-case err
|
|
|
|
|
(xref--insert-xrefs
|
|
|
|
|
(xref--analyze (funcall xref--fetcher)))
|
|
|
|
|
(user-error
|
|
|
|
|
(insert
|
|
|
|
|
(propertize
|
|
|
|
|
(error-message-string err)
|
|
|
|
|
'face 'error))))
|
|
|
|
|
(goto-char (point-min)))))
|
|
|
|
|
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(defun xref--show-defs-buffer (fetcher alist)
|
|
|
|
|
(let ((xrefs (funcall fetcher)))
|
|
|
|
|
(cond
|
|
|
|
|
((not (cdr xrefs))
|
2019-06-09 17:13:40 +03:00
|
|
|
|
(xref-pop-to-location (car xrefs)
|
|
|
|
|
(assoc-default 'display-action alist)))
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(t
|
|
|
|
|
(xref--show-xref-buffer fetcher
|
|
|
|
|
(cons (cons 'fetched-xrefs xrefs)
|
|
|
|
|
alist))))))
|
2019-05-23 01:30:50 +03:00
|
|
|
|
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(defun xref--show-defs-buffer-at-bottom (fetcher alist)
|
|
|
|
|
"Show definitions list in a window at the bottom.
|
|
|
|
|
When there is more than one definition, split the selected window
|
|
|
|
|
and show the list in a small window at the bottom. And use a
|
|
|
|
|
local keymap that binds `RET' to `xref-quit-and-goto-xref'."
|
|
|
|
|
(let ((xrefs (funcall fetcher)))
|
|
|
|
|
(cond
|
|
|
|
|
((not (cdr xrefs))
|
2019-06-09 17:13:40 +03:00
|
|
|
|
(xref-pop-to-location (car xrefs)
|
|
|
|
|
(assoc-default 'display-action alist)))
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(t
|
|
|
|
|
(with-current-buffer (get-buffer-create xref-buffer-name)
|
|
|
|
|
(xref--transient-buffer-mode)
|
2019-06-10 03:10:34 +03:00
|
|
|
|
(xref--show-common-initialize (xref--analyze xrefs) fetcher alist)
|
2019-05-31 18:30:36 +03:00
|
|
|
|
(pop-to-buffer (current-buffer)
|
|
|
|
|
'(display-buffer-in-direction . ((direction . below))))
|
|
|
|
|
(current-buffer))))))
|
|
|
|
|
|
2020-12-05 02:14:32 +02:00
|
|
|
|
(defun xref--show-defs-minibuffer (fetcher alist)
|
|
|
|
|
(let* ((xrefs (funcall fetcher))
|
|
|
|
|
(xref-alist (xref--analyze xrefs))
|
|
|
|
|
xref-alist-with-line-info
|
2020-12-05 02:36:04 +02:00
|
|
|
|
xref
|
|
|
|
|
(group-prefix-length
|
|
|
|
|
;; FIXME: Groups are not always file names, but they often
|
|
|
|
|
;; are. At least this shouldn't make the other kinds of
|
|
|
|
|
;; groups look worse.
|
|
|
|
|
(let ((common-prefix (try-completion "" xref-alist)))
|
|
|
|
|
(if (> (length common-prefix) 0)
|
|
|
|
|
(length (file-name-directory common-prefix))
|
|
|
|
|
0))))
|
2020-12-05 02:14:32 +02:00
|
|
|
|
|
|
|
|
|
(cl-loop for ((group . xrefs) . more1) on xref-alist
|
|
|
|
|
do
|
2020-12-05 02:57:46 +02:00
|
|
|
|
(cl-loop for (xref . more2) on xrefs do
|
|
|
|
|
(with-slots (summary location) xref
|
|
|
|
|
(let* ((line (xref-location-line location))
|
|
|
|
|
(line-fmt
|
|
|
|
|
(if line
|
|
|
|
|
(format #("%d:" 0 2 (face xref-line-number))
|
|
|
|
|
line)
|
|
|
|
|
""))
|
|
|
|
|
(group-fmt
|
|
|
|
|
(propertize
|
|
|
|
|
(substring group group-prefix-length)
|
|
|
|
|
'face 'xref-file-header))
|
|
|
|
|
(candidate
|
|
|
|
|
(format "%s:%s%s" group-fmt line-fmt summary)))
|
|
|
|
|
(push (cons candidate xref) xref-alist-with-line-info)))))
|
2020-12-05 02:14:32 +02:00
|
|
|
|
|
|
|
|
|
(setq xref (if (not (cdr xrefs))
|
|
|
|
|
(car xrefs)
|
|
|
|
|
(cdr (assoc (completing-read "Jump to definition: "
|
|
|
|
|
(reverse xref-alist-with-line-info))
|
|
|
|
|
xref-alist-with-line-info))))
|
|
|
|
|
|
|
|
|
|
(xref-pop-to-location xref (assoc-default 'display-action alist))))
|
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2019-06-25 13:09:41 +02:00
|
|
|
|
(defcustom xref-show-xrefs-function 'xref--show-xref-buffer
|
2019-05-26 00:14:10 +03:00
|
|
|
|
"Function to display a list of search results.
|
|
|
|
|
|
|
|
|
|
It should accept two arguments: FETCHER and ALIST.
|
|
|
|
|
|
|
|
|
|
FETCHER is a function of no arguments that returns a list of xref
|
|
|
|
|
values. It must not depend on the current buffer or selected
|
|
|
|
|
window.
|
|
|
|
|
|
2019-05-28 01:09:17 +03:00
|
|
|
|
ALIST can include, but limited to, the following keys:
|
2019-05-26 00:14:10 +03:00
|
|
|
|
|
|
|
|
|
WINDOW for the window that was selected before the current
|
|
|
|
|
command was called.
|
|
|
|
|
|
|
|
|
|
DISPLAY-ACTION indicates where the target location should be
|
|
|
|
|
displayed. The possible values are nil, `window' meaning the
|
2019-06-25 13:09:41 +02:00
|
|
|
|
other window, or `frame' meaning the other frame."
|
|
|
|
|
:type 'function)
|
2019-05-23 01:16:41 +03:00
|
|
|
|
|
2019-06-25 13:09:41 +02:00
|
|
|
|
(defcustom xref-show-definitions-function 'xref--show-defs-buffer
|
2019-05-26 00:14:10 +03:00
|
|
|
|
"Function to display a list of definitions.
|
|
|
|
|
|
2019-06-25 13:09:41 +02:00
|
|
|
|
Accepts the same arguments as `xref-show-xrefs-function'."
|
|
|
|
|
:type 'function)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
2015-01-22 04:09:23 +02:00
|
|
|
|
(defvar xref--read-identifier-history nil)
|
|
|
|
|
|
|
|
|
|
(defvar xref--read-pattern-history nil)
|
|
|
|
|
|
2020-08-27 11:32:50 +03:00
|
|
|
|
(defun xref--show-xrefs (fetcher display-action &optional _always-show-list)
|
2019-05-23 01:30:50 +03:00
|
|
|
|
(xref--push-markers)
|
2020-08-27 00:54:28 +03:00
|
|
|
|
(unless (functionp fetcher)
|
|
|
|
|
;; Old convention.
|
|
|
|
|
(let ((xrefs fetcher))
|
|
|
|
|
(setq fetcher
|
|
|
|
|
(lambda ()
|
|
|
|
|
(if (eq xrefs 'called-already)
|
|
|
|
|
(user-error "Refresh is not supported")
|
|
|
|
|
(prog1
|
|
|
|
|
xrefs
|
|
|
|
|
(setq xrefs 'called-already)))))))
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(funcall xref-show-xrefs-function fetcher
|
2019-05-23 01:16:41 +03:00
|
|
|
|
`((window . ,(selected-window))
|
|
|
|
|
(display-action . ,display-action))))
|
|
|
|
|
|
|
|
|
|
(defun xref--show-defs (xrefs display-action)
|
2019-05-23 01:30:50 +03:00
|
|
|
|
(xref--push-markers)
|
|
|
|
|
(funcall xref-show-definitions-function xrefs
|
|
|
|
|
`((window . ,(selected-window))
|
|
|
|
|
(display-action . ,display-action))))
|
|
|
|
|
|
|
|
|
|
(defun xref--push-markers ()
|
2019-03-24 23:19:55 +02:00
|
|
|
|
(unless (region-active-p) (push-mark nil t))
|
2019-05-23 01:30:50 +03:00
|
|
|
|
(xref-push-marker-stack))
|
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."
|
2015-11-14 02:37:01 +02:00
|
|
|
|
(let* ((backend (xref-find-backend))
|
2020-01-18 00:14:24 +03:00
|
|
|
|
(def (xref-backend-identifier-at-point backend))
|
|
|
|
|
(completion-ignore-case
|
|
|
|
|
(xref-backend-identifier-completion-ignore-case backend)))
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(cond ((or current-prefix-arg
|
2019-05-03 01:29:59 +03:00
|
|
|
|
(not def)
|
2015-05-26 19:28:38 +03:00
|
|
|
|
(xref--prompt-p this-command))
|
2019-05-03 01:29:59 +03:00
|
|
|
|
(let ((id
|
|
|
|
|
(completing-read
|
|
|
|
|
(if def
|
|
|
|
|
(format "%s (default %s): "
|
|
|
|
|
(substring prompt 0 (string-match
|
|
|
|
|
"[ :]+\\'" prompt))
|
|
|
|
|
def)
|
|
|
|
|
prompt)
|
|
|
|
|
(xref-backend-identifier-completion-table backend)
|
|
|
|
|
nil nil nil
|
|
|
|
|
'xref--read-identifier-history def)))
|
|
|
|
|
(if (equal id "")
|
2019-08-15 00:06:07 +03:00
|
|
|
|
(or def (user-error "There is no default identifier"))
|
2019-05-03 01:29:59 +03:00
|
|
|
|
id)))
|
|
|
|
|
(t def))))
|
2014-12-25 22:08:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Commands
|
|
|
|
|
|
2016-02-22 00:26:24 +02:00
|
|
|
|
(defun xref--find-xrefs (input kind arg display-action)
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(xref--show-xrefs
|
|
|
|
|
(xref--create-fetcher input kind arg)
|
|
|
|
|
display-action))
|
|
|
|
|
|
|
|
|
|
(defun xref--find-definitions (id display-action)
|
|
|
|
|
(xref--show-defs
|
|
|
|
|
(xref--create-fetcher id 'definitions id)
|
|
|
|
|
display-action))
|
|
|
|
|
|
|
|
|
|
(defun xref--create-fetcher (input kind arg)
|
2019-05-26 00:14:10 +03:00
|
|
|
|
"Return an xref list fetcher function.
|
|
|
|
|
|
|
|
|
|
It revisits the saved position and delegates the finding logic to
|
|
|
|
|
the xref backend method indicated by KIND and passes ARG to it."
|
2019-05-24 04:50:44 +03:00
|
|
|
|
(let* ((orig-buffer (current-buffer))
|
|
|
|
|
(orig-position (point))
|
|
|
|
|
(backend (xref-find-backend))
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(method (intern (format "xref-backend-%s" kind))))
|
|
|
|
|
(lambda ()
|
|
|
|
|
(save-excursion
|
2019-05-26 00:18:12 +03:00
|
|
|
|
;; Xref methods are generally allowed to depend on the text
|
|
|
|
|
;; around point, not just on their explicit arguments.
|
|
|
|
|
;;
|
|
|
|
|
;; There is only so much we can do, however, to recreate that
|
|
|
|
|
;; context, given that the user is free to change the buffer
|
|
|
|
|
;; contents freely in the meantime.
|
2019-05-25 00:11:41 +03:00
|
|
|
|
(when (buffer-live-p orig-buffer)
|
|
|
|
|
(set-buffer orig-buffer)
|
|
|
|
|
(ignore-errors (goto-char orig-position)))
|
|
|
|
|
(let ((xrefs (funcall method backend arg)))
|
|
|
|
|
(unless xrefs
|
|
|
|
|
(xref--not-found-error kind input))
|
|
|
|
|
xrefs)))))
|
2019-05-23 01:16:41 +03:00
|
|
|
|
|
|
|
|
|
(defun xref--not-found-error (kind input)
|
|
|
|
|
(user-error "No %s found for: %s" (symbol-name kind) input))
|
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,
|
2015-08-11 14:28:17 -05:00
|
|
|
|
prompt for it.
|
|
|
|
|
|
2016-01-09 19:06:52 +02:00
|
|
|
|
If sufficient information is available to determine a unique
|
|
|
|
|
definition for IDENTIFIER, display it in the selected window.
|
|
|
|
|
Otherwise, display the list of the possible definitions in a
|
|
|
|
|
buffer where the user can select from the list."
|
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.
|
2018-08-07 19:15:41 +03:00
|
|
|
|
This command might prompt for the identifier as needed, perhaps
|
|
|
|
|
offering the symbol at point as the default.
|
|
|
|
|
With prefix argument, or if `xref-prompt-for-identifier' is t,
|
|
|
|
|
always prompt for the identifier. If `xref-prompt-for-identifier'
|
|
|
|
|
is nil, prompt only if there's no usable symbol at point."
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(interactive (list (xref--read-identifier "Find references of: ")))
|
2015-11-06 05:08:51 +02:00
|
|
|
|
(xref--find-xrefs identifier 'references identifier nil))
|
2015-05-11 02:07:27 +03:00
|
|
|
|
|
2018-07-07 11:59:56 +03:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-find-definitions-at-mouse (event)
|
|
|
|
|
"Find the definition of identifier at or around mouse click.
|
|
|
|
|
This command is intended to be bound to a mouse event."
|
|
|
|
|
(interactive "e")
|
|
|
|
|
(let ((identifier
|
|
|
|
|
(save-excursion
|
|
|
|
|
(mouse-set-point event)
|
|
|
|
|
(xref-backend-identifier-at-point (xref-find-backend)))))
|
|
|
|
|
(if identifier
|
|
|
|
|
(xref-find-definitions identifier)
|
|
|
|
|
(user-error "No identifier here"))))
|
|
|
|
|
|
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)
|
2020-06-01 04:44:33 +03:00
|
|
|
|
(let* ((newpat
|
|
|
|
|
(if (and (version< emacs-version "28.0.50")
|
|
|
|
|
(memq (xref-find-backend) '(elisp etags)))
|
|
|
|
|
;; Handle backends in older Emacs.
|
|
|
|
|
(xref-apropos-regexp pattern)
|
|
|
|
|
;; Delegate pattern handling to the backend fully.
|
|
|
|
|
;; The old way didn't work for "external" backends.
|
|
|
|
|
pattern)))
|
|
|
|
|
(xref--find-xrefs pattern 'apropos newpat nil)))
|
|
|
|
|
|
|
|
|
|
(defun xref-apropos-regexp (pattern)
|
|
|
|
|
"Return an Emacs regexp from PATTERN similar to `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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Key bindings
|
|
|
|
|
|
|
|
|
|
;;;###autoload (define-key esc-map "." #'xref-find-definitions)
|
|
|
|
|
;;;###autoload (define-key esc-map "," #'xref-pop-marker-stack)
|
2015-07-13 04:27:32 +03:00
|
|
|
|
;;;###autoload (define-key esc-map "?" #'xref-find-references)
|
2014-12-25 22:08:19 +02:00
|
|
|
|
;;;###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
|
2015-11-14 02:40:06 +02:00
|
|
|
|
(setq xref-etags-mode--saved xref-backend-functions)
|
|
|
|
|
(kill-local-variable 'xref-backend-functions))
|
|
|
|
|
(setq-local xref-backend-functions xref-etags-mode--saved)))
|
2015-02-23 04:00:01 +02:00
|
|
|
|
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(declare-function semantic-symref-instantiate "semantic/symref")
|
|
|
|
|
(declare-function semantic-symref-perform-search "semantic/symref")
|
2015-07-13 04:27:32 +03:00
|
|
|
|
(declare-function grep-expand-template "grep")
|
2016-01-29 17:43:26 -06:00
|
|
|
|
(defvar ede-minor-mode) ;; ede.el
|
2015-05-01 21:54:33 +03:00
|
|
|
|
|
2019-12-29 15:38:17 +03:00
|
|
|
|
;;;###autoload
|
2019-12-29 15:44:08 +03:00
|
|
|
|
(defun xref-references-in-directory (symbol dir)
|
2019-12-28 20:07:25 +03:00
|
|
|
|
"Find all references to SYMBOL in directory DIR.
|
|
|
|
|
Return a list of xref values.
|
|
|
|
|
|
2015-06-02 18:46:42 +03:00
|
|
|
|
This function uses the Semantic Symbol Reference API, see
|
2016-04-12 21:08:22 +03:00
|
|
|
|
`semantic-symref-tool-alist' for details on which tools are used,
|
|
|
|
|
and when."
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(cl-assert (directory-name-p dir))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(require 'semantic/symref)
|
|
|
|
|
(defvar semantic-symref-tool)
|
2016-01-22 01:53:05 -06:00
|
|
|
|
|
|
|
|
|
;; Some symref backends use `ede-project-root-directory' as the root
|
|
|
|
|
;; directory for the search, rather than `default-directory'. Since
|
|
|
|
|
;; the caller has specified `dir', we bind `ede-minor-mode' to nil
|
|
|
|
|
;; to force the backend to use `default-directory'.
|
|
|
|
|
(let* ((ede-minor-mode nil)
|
|
|
|
|
(default-directory dir)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
;; 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.
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(semantic-symref-tool 'detect)
|
2016-03-03 02:36:27 +02:00
|
|
|
|
(case-fold-search nil)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(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)))))
|
2015-06-02 18:46:42 +03:00
|
|
|
|
|
2019-12-29 15:44:08 +03:00
|
|
|
|
(define-obsolete-function-alias
|
|
|
|
|
'xref-collect-references
|
|
|
|
|
#'xref-references-in-directory
|
|
|
|
|
"27.1")
|
|
|
|
|
|
2016-01-18 22:11:46 +03:00
|
|
|
|
;;;###autoload
|
2019-12-29 15:44:08 +03:00
|
|
|
|
(defun xref-matches-in-directory (regexp files dir ignores)
|
2019-12-29 14:28:22 +03:00
|
|
|
|
"Find all matches for REGEXP in directory DIR.
|
2019-12-28 20:07:25 +03:00
|
|
|
|
Return a list of xref values.
|
2019-12-29 14:28:22 +03:00
|
|
|
|
Only files matching some of FILES and none of IGNORES are searched.
|
2015-07-12 18:35:08 +03:00
|
|
|
|
FILES is a string with glob patterns separated by spaces.
|
2019-12-29 14:28:22 +03:00
|
|
|
|
IGNORES is a list of glob patterns for files to ignore."
|
2016-01-18 22:11:46 +03:00
|
|
|
|
;; DIR can also be a regular file for now; let's not advertise that.
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(grep-compute-defaults)
|
|
|
|
|
(defvar grep-find-template)
|
2015-07-11 18:56:42 +03:00
|
|
|
|
(defvar grep-highlight-matches)
|
2016-09-13 20:48:09 -04:00
|
|
|
|
(pcase-let*
|
|
|
|
|
((grep-find-template (replace-regexp-in-string "<C>" "<C> -E"
|
|
|
|
|
grep-find-template t t))
|
|
|
|
|
(grep-highlight-matches nil)
|
|
|
|
|
;; TODO: Sanitize the regexp to remove Emacs-specific terms,
|
|
|
|
|
;; so that Grep can search for the "relaxed" version. Can we
|
|
|
|
|
;; do that reliably enough, without creating false negatives?
|
|
|
|
|
(command (xref--rgrep-command (xref--regexp-to-extended regexp)
|
|
|
|
|
files
|
2018-10-29 14:09:52 +01:00
|
|
|
|
(file-local-name (expand-file-name dir))
|
2016-09-13 20:48:09 -04:00
|
|
|
|
ignores))
|
2017-09-26 01:44:54 +03:00
|
|
|
|
(def default-directory)
|
2016-09-13 20:48:09 -04:00
|
|
|
|
(buf (get-buffer-create " *xref-grep*"))
|
2017-07-30 14:47:05 -04:00
|
|
|
|
(`(,grep-re ,file-group ,line-group . ,_) (car grep-regexp-alist))
|
2016-09-13 20:48:09 -04:00
|
|
|
|
(status nil)
|
|
|
|
|
(hits nil))
|
2015-06-02 18:46:42 +03:00
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(erase-buffer)
|
2017-09-26 01:44:54 +03:00
|
|
|
|
(setq default-directory def)
|
2017-05-29 02:55:42 +03:00
|
|
|
|
(setq status
|
2018-10-29 14:09:52 +01:00
|
|
|
|
(process-file-shell-command command nil t))
|
2015-07-10 04:38:16 +03:00
|
|
|
|
(goto-char (point-min))
|
2017-05-30 00:58:39 +03:00
|
|
|
|
;; Can't use the exit status: Grep exits with 1 to mean "no
|
|
|
|
|
;; matches found". Find exits with 1 if any of the invocations
|
|
|
|
|
;; exit with non-zero. "No matches" and "Grep program not found"
|
|
|
|
|
;; are all the same to it.
|
|
|
|
|
(when (and (/= (point-min) (point-max))
|
|
|
|
|
(not (looking-at grep-re)))
|
|
|
|
|
(user-error "Search failed with status %d: %s" status (buffer-string)))
|
2015-07-10 04:38:16 +03:00
|
|
|
|
(while (re-search-forward grep-re nil t)
|
2016-09-13 20:48:09 -04:00
|
|
|
|
(push (list (string-to-number (match-string line-group))
|
|
|
|
|
(match-string file-group)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(buffer-substring-no-properties (point) (line-end-position)))
|
2015-07-10 04:38:16 +03:00
|
|
|
|
hits)))
|
2016-05-02 02:38:01 +03:00
|
|
|
|
(xref--convert-hits (nreverse hits) regexp)))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
|
2019-12-29 15:44:08 +03:00
|
|
|
|
(define-obsolete-function-alias
|
|
|
|
|
'xref-collect-matches
|
|
|
|
|
#'xref-matches-in-directory
|
|
|
|
|
"27.1")
|
|
|
|
|
|
2020-01-07 16:30:25 +03:00
|
|
|
|
(declare-function tramp-tramp-file-p "tramp")
|
|
|
|
|
(declare-function tramp-file-local-name "tramp")
|
|
|
|
|
|
2020-12-04 03:37:10 +02:00
|
|
|
|
;; TODO: Experiment with 'xargs -P4' (or any other number).
|
|
|
|
|
;; This speeds up either command, even more than rg's '-j4' does.
|
|
|
|
|
;; Ripgrep gets jumbled output, though, even with --line-buffered.
|
|
|
|
|
;; But Grep seems to be stable. Even without --line-buffered.
|
|
|
|
|
(defcustom xref-search-program-alist
|
|
|
|
|
'((grep
|
|
|
|
|
.
|
|
|
|
|
;; '-s' because 'git ls-files' can output broken symlinks.
|
|
|
|
|
"xargs -0 grep <C> -snHE -e <R>")
|
|
|
|
|
(ripgrep
|
|
|
|
|
.
|
|
|
|
|
;; Note: by default, ripgrep's output order is non-deterministic
|
|
|
|
|
;; (https://github.com/BurntSushi/ripgrep/issues/152)
|
|
|
|
|
;; because it does the search in parallel. You can use the template
|
|
|
|
|
;; without the '| sort ...' part if GNU sort is not available on
|
|
|
|
|
;; your system and/or stable ordering is not important to you.
|
|
|
|
|
;; Note#2: '!*/' is there to filter out dirs (e.g. submodules).
|
2020-12-04 04:46:52 +02:00
|
|
|
|
"xargs -0 rg <C> -nH --no-messages -g '!*/' -e <R> | sort -t: -k1,1 -k2n,2"
|
2020-12-04 03:37:10 +02:00
|
|
|
|
))
|
|
|
|
|
"Associative list mapping program identifiers to command templates.
|
|
|
|
|
|
|
|
|
|
Program identifier should be a symbol, named after the search program.
|
|
|
|
|
|
|
|
|
|
The command template must be a shell command (or usually a
|
2020-12-04 23:22:22 +02:00
|
|
|
|
pipeline) that will search the files based on the list of file
|
|
|
|
|
names that is piped from stdin, separated by null characters.
|
|
|
|
|
The template should have the following fields:
|
2020-12-04 03:37:10 +02:00
|
|
|
|
|
|
|
|
|
<C> for extra arguments such as -i and --color
|
|
|
|
|
<R> for the regexp itself (in Extended format)"
|
|
|
|
|
:type '(repeat
|
|
|
|
|
(cons (symbol :tag "Program identifier")
|
2020-12-09 21:42:19 +02:00
|
|
|
|
(string :tag "Command template")))
|
|
|
|
|
:version "28.1"
|
|
|
|
|
:package-version '(xref . "1.0.4"))
|
2020-12-04 03:37:10 +02:00
|
|
|
|
|
|
|
|
|
(defcustom xref-search-program 'grep
|
2020-12-04 23:22:22 +02:00
|
|
|
|
"The program to use for regexp search inside files.
|
2020-12-04 03:37:10 +02:00
|
|
|
|
|
|
|
|
|
This must reference a corresponding entry in `xref-search-program-alist'."
|
|
|
|
|
:type `(choice
|
|
|
|
|
(const :tag "Use Grep" grep)
|
|
|
|
|
(const :tag "Use ripgrep" ripgrep)
|
2020-12-09 21:42:19 +02:00
|
|
|
|
(symbol :tag "User defined"))
|
|
|
|
|
:version "28.1"
|
|
|
|
|
:package-version '(xref . "1.0.4"))
|
2020-12-04 03:37:10 +02:00
|
|
|
|
|
2019-12-29 15:11:53 +03:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun xref-matches-in-files (regexp files)
|
|
|
|
|
"Find all matches for REGEXP in FILES.
|
|
|
|
|
Return a list of xref values.
|
|
|
|
|
FILES must be a list of absolute file names."
|
2020-03-30 23:16:03 +03:00
|
|
|
|
(cl-assert (consp files))
|
2020-12-04 03:37:10 +02:00
|
|
|
|
(require 'grep)
|
|
|
|
|
(defvar grep-highlight-matches)
|
2019-12-29 15:11:53 +03:00
|
|
|
|
(pcase-let*
|
|
|
|
|
((output (get-buffer-create " *project grep output*"))
|
|
|
|
|
(`(,grep-re ,file-group ,line-group . ,_) (car grep-regexp-alist))
|
|
|
|
|
(status nil)
|
|
|
|
|
(hits nil)
|
|
|
|
|
;; Support for remote files. The assumption is that, if the
|
|
|
|
|
;; first file is remote, they all are, and on the same host.
|
|
|
|
|
(dir (file-name-directory (car files)))
|
|
|
|
|
(remote-id (file-remote-p dir))
|
2020-12-04 03:37:10 +02:00
|
|
|
|
;; The 'auto' default would be fine too, but ripgrep can't handle
|
|
|
|
|
;; the options we pass in that case.
|
|
|
|
|
(grep-highlight-matches nil)
|
|
|
|
|
(command (grep-expand-template (cdr
|
|
|
|
|
(or
|
|
|
|
|
(assoc
|
|
|
|
|
xref-search-program
|
|
|
|
|
xref-search-program-alist)
|
|
|
|
|
(user-error "Unknown search program `%s'"
|
|
|
|
|
xref-search-program)))
|
|
|
|
|
(xref--regexp-to-extended regexp))))
|
2019-12-29 15:11:53 +03:00
|
|
|
|
(when remote-id
|
2020-01-07 16:30:25 +03:00
|
|
|
|
(require 'tramp)
|
|
|
|
|
(setq files (mapcar
|
|
|
|
|
(if (tramp-tramp-file-p dir)
|
|
|
|
|
#'tramp-file-local-name
|
|
|
|
|
#'file-local-name)
|
|
|
|
|
files)))
|
2019-12-29 15:11:53 +03:00
|
|
|
|
(with-current-buffer output
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert (mapconcat #'identity files "\0"))
|
|
|
|
|
(setq default-directory dir)
|
|
|
|
|
(setq status
|
2020-08-14 10:20:04 +03:00
|
|
|
|
(xref--process-file-region (point-min)
|
|
|
|
|
(point-max)
|
|
|
|
|
shell-file-name
|
|
|
|
|
output
|
|
|
|
|
nil
|
|
|
|
|
shell-command-switch
|
|
|
|
|
command)))
|
2019-12-29 15:11:53 +03:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(when (and (/= (point-min) (point-max))
|
|
|
|
|
(not (looking-at grep-re))
|
|
|
|
|
;; TODO: Show these matches as well somehow?
|
|
|
|
|
(not (looking-at "Binary file .* matches")))
|
|
|
|
|
(user-error "Search failed with status %d: %s" status
|
|
|
|
|
(buffer-substring (point-min) (line-end-position))))
|
|
|
|
|
(while (re-search-forward grep-re nil t)
|
|
|
|
|
(push (list (string-to-number (match-string line-group))
|
|
|
|
|
(match-string file-group)
|
|
|
|
|
(buffer-substring-no-properties (point) (line-end-position)))
|
|
|
|
|
hits)))
|
|
|
|
|
(xref--convert-hits (nreverse hits) regexp)))
|
|
|
|
|
|
2020-08-14 10:20:04 +03:00
|
|
|
|
(defun xref--process-file-region ( start end program
|
|
|
|
|
&optional buffer display
|
|
|
|
|
&rest args)
|
|
|
|
|
;; FIXME: This branching shouldn't be necessary, but
|
|
|
|
|
;; call-process-region *is* measurably faster, even for a program
|
|
|
|
|
;; doing some actual work (for a period of time). Even though
|
|
|
|
|
;; call-process-region also creates a temp file internally
|
2020-10-24 20:22:33 +02:00
|
|
|
|
;; (https://lists.gnu.org/archive/html/emacs-devel/2019-01/msg00211.html).
|
2020-08-14 10:20:04 +03:00
|
|
|
|
(if (not (file-remote-p default-directory))
|
|
|
|
|
(apply #'call-process-region
|
|
|
|
|
start end program nil buffer display args)
|
|
|
|
|
(let ((infile (make-temp-file "ppfr")))
|
|
|
|
|
(unwind-protect
|
|
|
|
|
(progn
|
|
|
|
|
(write-region start end infile nil 'silent)
|
|
|
|
|
(apply #'process-file program infile buffer display args))
|
|
|
|
|
(delete-file infile)))))
|
|
|
|
|
|
2015-07-12 17:18:09 +03:00
|
|
|
|
(defun xref--rgrep-command (regexp files dir ignores)
|
|
|
|
|
(require 'find-dired) ; for `find-name-arg'
|
|
|
|
|
(defvar grep-find-template)
|
|
|
|
|
(defvar find-name-arg)
|
2016-05-05 04:15:23 +03:00
|
|
|
|
;; `shell-quote-argument' quotes the tilde as well.
|
|
|
|
|
(cl-assert (not (string-match-p "\\`~" dir)))
|
2015-07-12 17:18:09 +03:00
|
|
|
|
(grep-expand-template
|
|
|
|
|
grep-find-template
|
|
|
|
|
regexp
|
|
|
|
|
(concat (shell-quote-argument "(")
|
|
|
|
|
" " find-name-arg " "
|
|
|
|
|
(mapconcat
|
|
|
|
|
#'shell-quote-argument
|
|
|
|
|
(split-string files)
|
|
|
|
|
(concat " -o " find-name-arg " "))
|
|
|
|
|
" "
|
|
|
|
|
(shell-quote-argument ")"))
|
2016-05-05 04:15:23 +03:00
|
|
|
|
(shell-quote-argument dir)
|
2016-01-07 20:14:40 +03:00
|
|
|
|
(xref--find-ignores-arguments ignores dir)))
|
|
|
|
|
|
|
|
|
|
(defun xref--find-ignores-arguments (ignores dir)
|
2016-01-29 17:43:26 -06:00
|
|
|
|
"Convert IGNORES and DIR to a list of arguments for 'find'.
|
|
|
|
|
IGNORES is a list of glob patterns. DIR is an absolute
|
|
|
|
|
directory, used as the root of the ignore globs."
|
2016-01-07 20:14:40 +03:00
|
|
|
|
(cl-assert (not (string-match-p "\\`~" dir)))
|
2019-05-03 02:48:44 +03:00
|
|
|
|
(if (not ignores)
|
|
|
|
|
""
|
2016-01-18 22:11:46 +03:00
|
|
|
|
(concat
|
|
|
|
|
(shell-quote-argument "(")
|
|
|
|
|
" -path "
|
|
|
|
|
(mapconcat
|
|
|
|
|
(lambda (ignore)
|
|
|
|
|
(when (string-match-p "/\\'" ignore)
|
|
|
|
|
(setq ignore (concat ignore "*")))
|
2020-05-15 14:49:46 -04:00
|
|
|
|
(shell-quote-argument (if (string-match "\\`\\./" ignore)
|
|
|
|
|
(replace-match dir t t ignore)
|
|
|
|
|
(if (string-prefix-p "*" ignore)
|
|
|
|
|
ignore
|
|
|
|
|
(concat "*/" ignore)))))
|
2016-01-18 22:11:46 +03:00
|
|
|
|
ignores
|
|
|
|
|
" -o -path ")
|
|
|
|
|
" "
|
|
|
|
|
(shell-quote-argument ")")
|
|
|
|
|
" -prune -o ")))
|
2015-07-12 17:18:09 +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))
|
|
|
|
|
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(defun xref--regexp-syntax-dependent-p (str)
|
|
|
|
|
"Return non-nil when STR depends on the buffer's syntax.
|
|
|
|
|
Such as the current syntax table and the applied syntax properties."
|
|
|
|
|
(let ((case-fold-search nil))
|
|
|
|
|
(string-match-p (rx
|
|
|
|
|
(or string-start (not (in ?\\)))
|
|
|
|
|
(0+ (= 2 ?\\))
|
|
|
|
|
?\\
|
|
|
|
|
(in ?b ?B ?< ?> ?w ?W ?_ ?s ?S))
|
|
|
|
|
str)))
|
|
|
|
|
|
2019-12-28 19:28:30 +03:00
|
|
|
|
(defvar xref--last-file-buffer nil)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(defvar xref--temp-buffer-file-name nil)
|
|
|
|
|
|
|
|
|
|
(defun xref--convert-hits (hits regexp)
|
2019-12-28 19:28:30 +03:00
|
|
|
|
(let (xref--last-file-buffer
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(tmp-buffer (generate-new-buffer " *xref-temp*")))
|
|
|
|
|
(unwind-protect
|
2020-06-21 14:31:16 +01:00
|
|
|
|
(mapcan (lambda (hit) (xref--collect-matches hit regexp tmp-buffer))
|
|
|
|
|
hits)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(kill-buffer tmp-buffer))))
|
|
|
|
|
|
|
|
|
|
(defun xref--collect-matches (hit regexp tmp-buffer)
|
|
|
|
|
(pcase-let* ((`(,line ,file ,text) hit)
|
2019-12-27 17:08:53 +03:00
|
|
|
|
(remote-id (file-remote-p default-directory))
|
|
|
|
|
(file (and file (concat remote-id file)))
|
2019-12-28 19:28:30 +03:00
|
|
|
|
(buf (xref--find-file-buffer file))
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(syntax-needed (xref--regexp-syntax-dependent-p regexp)))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(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)
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(line-end-position)
|
|
|
|
|
syntax-needed)))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
;; Using the temporary buffer is both a performance and a buffer
|
|
|
|
|
;; management optimization.
|
|
|
|
|
(with-current-buffer tmp-buffer
|
|
|
|
|
(erase-buffer)
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(when (and syntax-needed
|
|
|
|
|
(not (equal file xref--temp-buffer-file-name)))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(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)
|
2015-05-01 21:54:33 +03:00
|
|
|
|
(goto-char (point-min))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(xref--collect-matches-1 regexp file line
|
|
|
|
|
(point)
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(point-max)
|
|
|
|
|
syntax-needed)))))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(defun xref--collect-matches-1 (regexp file line line-beg line-end syntax-needed)
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(let (matches)
|
2017-05-02 00:09:09 +03:00
|
|
|
|
(when syntax-needed
|
|
|
|
|
(syntax-propertize line-end))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
;; FIXME: This results in several lines with the same
|
|
|
|
|
;; summary. Solve with composite pattern?
|
2016-05-04 01:59:29 +03:00
|
|
|
|
(while (and
|
|
|
|
|
;; REGEXP might match an empty string. Or line.
|
|
|
|
|
(or (null matches)
|
|
|
|
|
(> (point) line-beg))
|
|
|
|
|
(re-search-forward regexp line-end t))
|
2016-04-12 21:08:22 +03:00
|
|
|
|
(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)))
|
2019-04-14 00:54:14 +03:00
|
|
|
|
(add-face-text-property beg-column end-column 'xref-match
|
2016-04-12 21:08:22 +03:00
|
|
|
|
t summary)
|
|
|
|
|
(push (xref-make-match summary loc (- end-column beg-column))
|
|
|
|
|
matches)))
|
|
|
|
|
(nreverse matches)))
|
|
|
|
|
|
2019-12-28 19:28:30 +03:00
|
|
|
|
(defun xref--find-file-buffer (file)
|
|
|
|
|
(unless (equal (car xref--last-file-buffer) file)
|
|
|
|
|
(setq xref--last-file-buffer
|
|
|
|
|
;; `find-buffer-visiting' is considerably slower,
|
|
|
|
|
;; especially on remote files.
|
2019-12-27 19:17:48 +03:00
|
|
|
|
(cons file (get-file-buffer file))))
|
2019-12-28 19:28:30 +03:00
|
|
|
|
(cdr xref--last-file-buffer))
|
2015-05-01 21:54:33 +03:00
|
|
|
|
|
2014-12-25 22:08:19 +02:00
|
|
|
|
(provide 'xref)
|
|
|
|
|
|
|
|
|
|
;;; xref.el ends here
|