Skip to content

Commit 394ce95

Browse files
committed
Consolidate cross-referencing commands
Move autoloaded bindings for `M-.', `M-,', `C-x 4 .' and `C-x 5 .' from etags.el to xref.el. * progmodes/xref.el: New file. * progmodes/elisp-mode.el (elisp--identifier-types): New variable. (elisp--identifier-location): New function, extracted from `elisp--company-location'. (elisp--company-location): Use it. (elisp--identifier-completion-table): New variable. (elisp-completion-at-point): Use it. (emacs-lisp-mode): Set the local values of `xref-find-function' and `xref-identifier-completion-table-function'. (elisp-xref-find, elisp--xref-find-definitions) (elisp--xref-identifier-completion-table): New functions. * progmodes/etags.el (find-tag-marker-ring): Mark obsolete in favor of `xref--marker-ring'. (tags-lazy-completion-table): Autoload. (tags-reset-tags-tables): Use `xref-clear-marker-stack'. (find-tag-noselect): Use `xref-push-marker-stack'. (pop-tag-mark): Make an alias for `xref-pop-marker-stack'. (etags--xref-limit): New constant. (etags-xref-find, etags--xref-find-definitions): New functions.
1 parent ac54901 commit 394ce95

File tree

5 files changed

+682
-51
lines changed

5 files changed

+682
-51
lines changed

etc/NEWS

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,25 @@ By default, 32 spaces and four TABs are considered to be too much but
434434
`tildify-ignored-environments-alist' variables (as well as a few
435435
helper functions) obsolete.
436436

437+
** xref
438+
The new package provides generic framework and new commands to find
439+
and move to definitions, as well as pop back to the original location.
440+
441+
*** New key bindings
442+
`xref-find-definitions' replaces `find-tag' and provides an interface
443+
to pick one destination among several. Hence, `tags-toop-continue' is
444+
unbound. `xref-pop-marker-stack' replaces `pop-tag-mark', but uses an
445+
easier binding, which is now unoccupied (`M-,').
446+
`xref-find-definitions-other-window' replaces `find-tag-other-window'.
447+
`xref-find-definitions-other-frame' replaces `find-tag-other-frame'.
448+
`xref-find-apropos' replaces `find-tag-regexp'.
449+
450+
*** New variables
451+
`find-tag-marker-ring-length' is now an obsolete alias for
452+
`xref-marker-ring-length'. `find-tag-marker-ring' is now an obsolete
453+
alias for a private variable. `xref-push-marker-stack' and
454+
`xref-pop-marker-stack' should be used to mutate it instead.
455+
437456
** Obsolete packages
438457

439458
---

lisp/ChangeLog

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
1+
2014-12-25 Helmut Eller <eller.helmut@gmail.com>
2+
Dmitry Gutov <dgutov@yandex.ru>
3+
4+
Consolidate cross-referencing commands.
5+
6+
Move autoloaded bindings for `M-.', `M-,', `C-x 4 .' and
7+
`C-x 5 .' from etags.el to xref.el.
8+
9+
* progmodes/xref.el: New file.
10+
11+
* progmodes/elisp-mode.el (elisp--identifier-types): New variable.
12+
(elisp--identifier-location): New function, extracted from
13+
`elisp--company-location'.
14+
(elisp--company-location): Use it.
15+
(elisp--identifier-completion-table): New variable.
16+
(elisp-completion-at-point): Use it.
17+
(emacs-lisp-mode): Set the local values of `xref-find-function'
18+
and `xref-identifier-completion-table-function'.
19+
(elisp-xref-find, elisp--xref-find-definitions)
20+
(elisp--xref-identifier-completion-table): New functions.
21+
22+
* progmodes/etags.el (find-tag-marker-ring): Mark obsolete in
23+
favor of `xref--marker-ring'.
24+
(tags-lazy-completion-table): Autoload.
25+
(tags-reset-tags-tables): Use `xref-clear-marker-stack'.
26+
(find-tag-noselect): Use `xref-push-marker-stack'.
27+
(pop-tag-mark): Make an alias for `xref-pop-marker-stack'.
28+
(etags--xref-limit): New constant.
29+
(etags-xref-find, etags--xref-find-definitions): New functions.
30+
131
2014-12-25 Martin Rudalics <rudalics@gmx.at>
232

333
* cus-start.el (resize-mini-windows): Make it customizable.

lisp/progmodes/elisp-mode.el

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,15 @@ Blank lines separate paragraphs. Semicolons start comments.
227227
228228
\\{emacs-lisp-mode-map}"
229229
:group 'lisp
230+
(defvar xref-find-function)
231+
(defvar xref-identifier-completion-table-function)
230232
(lisp-mode-variables nil nil 'elisp)
231233
(setq imenu-case-fold-search nil)
232234
(setq-local eldoc-documentation-function
233235
#'elisp-eldoc-documentation-function)
236+
(setq-local xref-find-function #'elisp-xref-find)
237+
(setq-local xref-identifier-completion-table-function
238+
#'elisp--xref-identifier-completion-table)
234239
(add-hook 'completion-at-point-functions
235240
#'elisp-completion-at-point nil 'local))
236241

@@ -414,17 +419,39 @@ It can be quoted, or be inside a quoted form."
414419

415420
(declare-function find-library-name "find-func" (library))
416421

422+
(defvar elisp--identifier-types '(defun defvar feature defface))
423+
424+
(defun elisp--identifier-location (type sym)
425+
(pcase (cons type sym)
426+
(`(defun . ,(pred fboundp))
427+
(find-definition-noselect sym nil))
428+
(`(defvar . ,(pred boundp))
429+
(find-definition-noselect sym 'defvar))
430+
(`(defface . ,(pred facep))
431+
(find-definition-noselect sym 'defface))
432+
(`(feature . ,(pred featurep))
433+
(require 'find-func)
434+
(cons (find-file-noselect (find-library-name
435+
(symbol-name sym)))
436+
1))))
437+
417438
(defun elisp--company-location (str)
418-
(let ((sym (intern-soft str)))
419-
(cond
420-
((fboundp sym) (find-definition-noselect sym nil))
421-
((boundp sym) (find-definition-noselect sym 'defvar))
422-
((featurep sym)
423-
(require 'find-func)
424-
(cons (find-file-noselect (find-library-name
425-
(symbol-name sym)))
426-
0))
427-
((facep sym) (find-definition-noselect sym 'defface)))))
439+
(catch 'res
440+
(let ((sym (intern-soft str)))
441+
(when sym
442+
(dolist (type elisp--identifier-types)
443+
(let ((loc (elisp--identifier-location type sym)))
444+
(and loc (throw 'res loc))))))))
445+
446+
(defvar elisp--identifier-completion-table
447+
(apply-partially #'completion-table-with-predicate
448+
obarray
449+
(lambda (sym)
450+
(or (boundp sym)
451+
(fboundp sym)
452+
(featurep sym)
453+
(symbol-plist sym)))
454+
'strict))
428455

429456
(defun elisp-completion-at-point ()
430457
"Function used for `completion-at-point-functions' in `emacs-lisp-mode'."
@@ -466,13 +493,8 @@ It can be quoted, or be inside a quoted form."
466493
:company-docsig #'elisp--company-doc-string
467494
:company-location #'elisp--company-location))
468495
((elisp--form-quoted-p beg)
469-
(list nil obarray
470-
;; Don't include all symbols
471-
;; (bug#16646).
472-
:predicate (lambda (sym)
473-
(or (boundp sym)
474-
(fboundp sym)
475-
(symbol-plist sym)))
496+
;; Don't include all symbols (bug#16646).
497+
(list nil elisp--identifier-completion-table
476498
:annotation-function
477499
(lambda (str) (if (fboundp (intern-soft str)) " <f>"))
478500
:company-doc-buffer #'elisp--company-doc-buffer
@@ -548,6 +570,38 @@ It can be quoted, or be inside a quoted form."
548570
(define-obsolete-function-alias
549571
'lisp-completion-at-point 'elisp-completion-at-point "25.1")
550572

573+
;;; Xref backend
574+
575+
(declare-function xref-make-buffer-location "xref" (buffer position))
576+
(declare-function xref-make-bogus-location "xref" (message))
577+
(declare-function xref-make "xref" (description location))
578+
579+
(defun elisp-xref-find (action id)
580+
(when (eq action 'definitions)
581+
(let ((sym (intern-soft id)))
582+
(when sym
583+
(remove nil (elisp--xref-find-definitions sym))))))
584+
585+
(defun elisp--xref-find-definitions (symbol)
586+
(save-excursion
587+
(mapcar
588+
(lambda (type)
589+
(let ((loc
590+
(condition-case err
591+
(let ((buf-pos (elisp--identifier-location type symbol)))
592+
(when buf-pos
593+
(xref-make-buffer-location (car buf-pos)
594+
(or (cdr buf-pos) 1))))
595+
(error
596+
(xref-make-bogus-location (error-message-string err))))))
597+
(when loc
598+
(xref-make (format "(%s %s)" type symbol)
599+
loc))))
600+
elisp--identifier-types)))
601+
602+
(defun elisp--xref-identifier-completion-table ()
603+
elisp--identifier-completion-table)
604+
551605
;;; Elisp Interaction mode
552606

553607
(defvar lisp-interaction-mode-map

lisp/progmodes/etags.el

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
(require 'ring)
3030
(require 'button)
31+
(require 'xref)
3132

3233
;;;###autoload
3334
(defvar tags-file-name nil
@@ -141,11 +142,8 @@ Otherwise, `find-tag-default' is used."
141142
:group 'etags
142143
:type '(choice (const nil) function))
143144

144-
(defcustom find-tag-marker-ring-length 16
145-
"Length of marker rings `find-tag-marker-ring' and `tags-location-ring'."
146-
:group 'etags
147-
:type 'integer
148-
:version "20.3")
145+
(define-obsolete-variable-alias 'find-tag-marker-ring-length
146+
'xref-marker-ring-length "25.1")
149147

150148
(defcustom tags-tag-face 'default
151149
"Face for tags in the output of `tags-apropos'."
@@ -182,15 +180,18 @@ Example value:
182180
(sexp :tag "Tags to search")))
183181
:version "21.1")
184182

185-
(defvar find-tag-marker-ring (make-ring find-tag-marker-ring-length)
186-
"Ring of markers which are locations from which \\[find-tag] was invoked.")
183+
(defvaralias 'find-tag-marker-ring 'xref--marker-ring)
184+
(make-obsolete-variable
185+
'find-tag-marker-ring
186+
"use `xref-push-marker-stack' or `xref-pop-marker-stack' instead."
187+
"25.1")
187188

188189
(defvar default-tags-table-function nil
189190
"If non-nil, a function to choose a default tags file for a buffer.
190191
This function receives no arguments and should return the default
191192
tags table file to use for the current buffer.")
192193

193-
(defvar tags-location-ring (make-ring find-tag-marker-ring-length)
194+
(defvar tags-location-ring (make-ring xref-marker-ring-length)
194195
"Ring of markers which are locations visited by \\[find-tag].
195196
Pop back to the last location with \\[negative-argument] \\[find-tag].")
196197

@@ -713,15 +714,13 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
713714
(interactive)
714715
;; Clear out the markers we are throwing away.
715716
(let ((i 0))
716-
(while (< i find-tag-marker-ring-length)
717+
(while (< i xref-marker-ring-length)
717718
(if (aref (cddr tags-location-ring) i)
718719
(set-marker (aref (cddr tags-location-ring) i) nil))
719-
(if (aref (cddr find-tag-marker-ring) i)
720-
(set-marker (aref (cddr find-tag-marker-ring) i) nil))
721720
(setq i (1+ i))))
721+
(xref-clear-marker-stack)
722722
(setq tags-file-name nil
723-
tags-location-ring (make-ring find-tag-marker-ring-length)
724-
find-tag-marker-ring (make-ring find-tag-marker-ring-length)
723+
tags-location-ring (make-ring xref-marker-ring-length)
725724
tags-table-list nil
726725
tags-table-computed-list nil
727726
tags-table-computed-list-for nil
@@ -780,6 +779,7 @@ tags table and its (recursively) included tags tables."
780779
(quit (message "Tags completion table construction aborted.")
781780
(setq tags-completion-table nil)))))
782781

782+
;;;###autoload
783783
(defun tags-lazy-completion-table ()
784784
(let ((buf (current-buffer)))
785785
(lambda (string pred action)
@@ -898,7 +898,7 @@ See documentation of variable `tags-file-name'."
898898
;; Run the user's hook. Do we really want to do this for pop?
899899
(run-hooks 'local-find-tag-hook))))
900900
;; Record whence we came.
901-
(ring-insert find-tag-marker-ring (point-marker))
901+
(xref-push-marker-stack)
902902
(if (and next-p last-tag)
903903
;; Find the same table we last used.
904904
(visit-tags-table-buffer 'same)
@@ -954,7 +954,6 @@ See documentation of variable `tags-file-name'."
954954
(switch-to-buffer buf)
955955
(error (pop-to-buffer buf)))
956956
(goto-char pos)))
957-
;;;###autoload (define-key esc-map "." 'find-tag)
958957

959958
;;;###autoload
960959
(defun find-tag-other-window (tagname &optional next-p regexp-p)
@@ -995,7 +994,6 @@ See documentation of variable `tags-file-name'."
995994
;; the window's point from the buffer.
996995
(set-window-point (selected-window) tagpoint))
997996
window-point)))
998-
;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
999997

1000998
;;;###autoload
1001999
(defun find-tag-other-frame (tagname &optional next-p)
@@ -1020,7 +1018,6 @@ See documentation of variable `tags-file-name'."
10201018
(interactive (find-tag-interactive "Find tag other frame: "))
10211019
(let ((pop-up-frames t))
10221020
(find-tag-other-window tagname next-p)))
1023-
;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
10241021

10251022
;;;###autoload
10261023
(defun find-tag-regexp (regexp &optional next-p other-window)
@@ -1044,25 +1041,10 @@ See documentation of variable `tags-file-name'."
10441041
;; We go through find-tag-other-window to do all the display hair there.
10451042
(funcall (if other-window 'find-tag-other-window 'find-tag)
10461043
regexp next-p t))
1047-
;;;###autoload (define-key esc-map [?\C-.] 'find-tag-regexp)
1048-
1049-
;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
10501044

10511045
;;;###autoload
1052-
(defun pop-tag-mark ()
1053-
"Pop back to where \\[find-tag] was last invoked.
1046+
(defalias 'pop-tag-mark 'xref-pop-marker-stack)
10541047

1055-
This is distinct from invoking \\[find-tag] with a negative argument
1056-
since that pops a stack of markers at which tags were found, not from
1057-
where they were found."
1058-
(interactive)
1059-
(if (ring-empty-p find-tag-marker-ring)
1060-
(error "No previous locations for find-tag invocation"))
1061-
(let ((marker (ring-remove find-tag-marker-ring 0)))
1062-
(switch-to-buffer (or (marker-buffer marker)
1063-
(error "The marked buffer has been deleted")))
1064-
(goto-char (marker-position marker))
1065-
(set-marker marker nil nil)))
10661048

10671049
(defvar tag-lines-already-matched nil
10681050
"Matches remembered between calls.") ; Doc string: calls to what?
@@ -1859,7 +1841,6 @@ nil, we exit; otherwise we scan the next file."
18591841
(and messaged
18601842
(null tags-loop-operate)
18611843
(message "Scanning file %s...found" buffer-file-name))))
1862-
;;;###autoload (define-key esc-map "," 'tags-loop-continue)
18631844

18641845
;;;###autoload
18651846
(defun tags-search (regexp &optional file-list-form)
@@ -2077,6 +2058,54 @@ for \\[find-tag] (which see)."
20772058
(completion-in-region (car comp-data) (cadr comp-data)
20782059
(nth 2 comp-data)
20792060
(plist-get (nthcdr 3 comp-data) :predicate)))))
2061+
2062+
2063+
;;; Xref backend
2064+
2065+
;; Stop searching if we find more than xref-limit matches, as the xref
2066+
;; infrastracture is not designed to handle very long lists.
2067+
;; Switching to some kind of lazy list might be better, but hopefully
2068+
;; we hit the limit rarely.
2069+
(defconst etags--xref-limit 1000)
2070+
2071+
;;;###autoload
2072+
(defun etags-xref-find (action id)
2073+
(pcase action
2074+
(`definitions (etags--xref-find-definitions id))
2075+
(`apropos (etags--xref-find-definitions id t))))
2076+
2077+
(defun etags--xref-find-definitions (pattern &optional regexp?)
2078+
;; This emulates the behaviour of `find-tag-in-order' but instead of
2079+
;; returning one match at a time all matches are returned as list.
2080+
;; NOTE: find-tag-tag-order is typically a buffer-local variable.
2081+
(let* ((xrefs '())
2082+
(first-time t)
2083+
(search-fun (if regexp? #'re-search-forward #'search-forward))
2084+
(marks (make-hash-table :test 'equal))
2085+
(case-fold-search (if (memq tags-case-fold-search '(nil t))
2086+
tags-case-fold-search
2087+
case-fold-search)))
2088+
(save-excursion
2089+
(while (visit-tags-table-buffer (not first-time))
2090+
(setq first-time nil)
2091+
(dolist (order-fun (cond (regexp? find-tag-regexp-tag-order)
2092+
(t find-tag-tag-order)))
2093+
(goto-char (point-min))
2094+
(while (and (funcall search-fun pattern nil t)
2095+
(< (hash-table-count marks) etags--xref-limit))
2096+
(when (funcall order-fun pattern)
2097+
(beginning-of-line)
2098+
(cl-destructuring-bind (hint line &rest pos) (etags-snarf-tag)
2099+
(unless (eq hint t) ; hint==t if we are in a filename line
2100+
(let* ((file (file-of-tag))
2101+
(mark-key (cons file line)))
2102+
(unless (gethash mark-key marks)
2103+
(let ((loc (xref-make-file-location
2104+
(expand-file-name file) line 0)))
2105+
(push (xref-make hint loc) xrefs)
2106+
(puthash mark-key t marks)))))))))))
2107+
(nreverse xrefs)))
2108+
20802109

20812110
(provide 'etags)
20822111

0 commit comments

Comments
 (0)