summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/find-func.el
diff options
context:
space:
mode:
authorDaniel Martín <mardani29@yahoo.es>2020-12-27 09:04:56 +0100
committerLars Ingebrigtsen <larsi@gnus.org>2020-12-27 09:04:56 +0100
commit80420faf4921ffe5e9d4c4f9595941acf3156e50 (patch)
tree3893cf5919ac80cfb6abdfebba8544a15c0316eb /lisp/emacs-lisp/find-func.el
parent0f790464d547dd57a857d88dab309b286067ac45 (diff)
downloademacs-80420faf4921ffe5e9d4c4f9595941acf3156e50.tar.gz
Improve "find definition" in *Help* buffers
* lisp/emacs-lisp/find-func.el (find-function-search-for-symbol): If our regexp algorithm could not find a location for the symbol definition, resort to find-function--search-by-expanding-macros. * test/lisp/emacs-lisp/find-func-tests.el: Add a automatic test for a function and variable generated by a macro. * etc/NEWS: Advertise the improved functionality (bug#45443).
Diffstat (limited to 'lisp/emacs-lisp/find-func.el')
-rw-r--r--lisp/emacs-lisp/find-func.el65
1 files changed, 64 insertions, 1 deletions
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 074e7db295b..7796a72ecfd 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -389,7 +389,70 @@ The search is done in the source for library LIBRARY."
(progn
(beginning-of-line)
(cons (current-buffer) (point)))
- (cons (current-buffer) nil)))))))))
+ ;; If the regexp search didn't find the location of
+ ;; the symbol (for example, because it is generated by
+ ;; a macro), try a slightly more expensive search that
+ ;; expands macros until it finds the symbol.
+ (cons (current-buffer)
+ (find-function--search-by-expanding-macros
+ (current-buffer) symbol type))))))))))
+
+(defun find-function--try-macroexpand (form)
+ "Try to macroexpand FORM in full or partially.
+This is a best-effort operation in which if macroexpansion fails,
+this function returns FORM as is."
+ (ignore-errors
+ (or
+ (macroexpand-all form)
+ (macroexpand-1 form)
+ form)))
+
+(defun find-function--any-subform-p (form pred)
+ "Walk FORM and apply PRED to its subexpressions.
+Return t if any PRED returns t."
+ (cond
+ ((not (consp form)) nil)
+ ((funcall pred form) t)
+ (t
+ (cl-destructuring-bind (left-child . right-child) form
+ (or
+ (find-function--any-subform-p left-child pred)
+ (find-function--any-subform-p right-child pred))))))
+
+(defun find-function--search-by-expanding-macros (buf symbol type)
+ "Expand macros in BUF to search for the definition of SYMBOL of TYPE."
+ (catch 'found
+ (with-current-buffer buf
+ (save-excursion
+ (goto-char (point-min))
+ (condition-case nil
+ (while t
+ (let ((form (read (current-buffer)))
+ (expected-symbol-p
+ (lambda (form)
+ (cond
+ ((null type)
+ ;; Check if a given form is a `defalias' to
+ ;; SYM, the function name we are searching
+ ;; for. All functions in Emacs Lisp
+ ;; ultimately expand to a `defalias' form
+ ;; after several steps of macroexpansion.
+ (and (eq (car-safe form) 'defalias)
+ (equal (car-safe (cdr form))
+ `(quote ,symbol))))
+ ((eq type 'defvar)
+ ;; Variables generated by macros ultimately
+ ;; expand to `defvar'.
+ (and (eq (car-safe form) 'defvar)
+ (eq (car-safe (cdr form)) symbol)))
+ (t nil)))))
+ (when (find-function--any-subform-p
+ (find-function--try-macroexpand form)
+ expected-symbol-p)
+ ;; We want to return the location at the beginning
+ ;; of the macro, so move back one sexp.
+ (throw 'found (progn (backward-sexp) (point))))))
+ (end-of-file nil))))))
(defun find-function-library (function &optional lisp-only verbose)
"Return the pair (ORIG-FUNCTION . LIBRARY) for FUNCTION.