aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Colascione2025-03-27 16:04:51 -0400
committerDaniel Colascione2025-03-27 16:21:13 -0400
commit364c3dbc12e7b6d41ab449dd495c96d08874310e (patch)
tree33b6c05a49a9f71b12ee4096afa78f5c067e4371
parent59fd8c26be2f7724a5c8cb583383f2025171c660 (diff)
downloademacs-364c3dbc12e7b6d41ab449dd495c96d08874310e.tar.gz
emacs-364c3dbc12e7b6d41ab449dd495c96d08874310e.zip
Help find-function find methods defined inside macros
* doc/lispref/functions.texi (Finding Definitions): Document the expanded definition-finding extension mechanism. * etc/NEWS: Briefly describe the new feature. * lisp/emacs-lisp/cl-generic.el (cl--generic-find-defgeneric-regexp): Use defconst now that we no longer have purespace. (cl--generic-search-method-make-form-matcher): New function. * lisp/emacs-lisp/find-func.el (find-function-regexp-alist) (find-function-search-for-symbol): Parse out the new factory function. (find-function--search-by-expanding-macros): Try using it when searching for definitions by expanding macros.
-rw-r--r--doc/lispref/functions.texi18
-rw-r--r--etc/NEWS3
-rw-r--r--lisp/emacs-lisp/cl-generic.el29
-rw-r--r--lisp/emacs-lisp/find-func.el61
4 files changed, 83 insertions, 28 deletions
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 1279b15b819..83acbff0885 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -849,9 +849,16 @@ The alist @code{find-function-regexp-alist} associates object types with
849a regexp or function that finds the definition of that object in its 849a regexp or function that finds the definition of that object in its
850source file. Each element's car is a symbol the describes the type of 850source file. Each element's car is a symbol the describes the type of
851object, or @code{nil} to identify functions defined with @code{defun}. 851object, or @code{nil} to identify functions defined with @code{defun}.
852Each element's cdr is a symbol: either the value of that symbol is a 852Each element's cdr can be:
853string interpreted as a regexp, or that symbol names a function that can 853
854find the definition. 854@itemize
855@item
856A symbol whose value is a string interpreted as a regexp
857@item
858A symbol naming a function that can find the definition
859@item
860A cons cell where the car is a regexp (or function that returns one) and the cdr is a function that creates a matcher for macroexpanded forms
861@end itemize
855 862
856A regexp string is actually a format string, and @code{%s} will be 863A regexp string is actually a format string, and @code{%s} will be
857substituted with the name of the symbol we are looking for. 864substituted with the name of the symbol we are looking for.
@@ -859,6 +866,11 @@ substituted with the name of the symbol we are looking for.
859A function will be called with one argument, the (symbol for) the object 866A function will be called with one argument, the (symbol for) the object
860we are searching for. 867we are searching for.
861 868
869The form-matcher function in a cons cell value is called with one argument (the
870symbol being sought) and should return a function that takes a form and returns
871non-nil if the form defines the sought symbol. This is useful for finding
872definitions that are created by macro expansion.
873
862@cindex @code{definition-name} (symbol property) 874@cindex @code{definition-name} (symbol property)
863If the function to be found is defined by a macro, it may be hard for 875If the function to be found is defined by a macro, it may be hard for
864Emacs to find the definition site in the source code. A macro call may 876Emacs to find the definition site in the source code. A macro call may
diff --git a/etc/NEWS b/etc/NEWS
index 1bd2fd6d486..823e4c0cde3 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -75,7 +75,8 @@ the 'standard-display-table's extra slots with Unicode characters.
75Please see the documentation of that function to see which slots of the 75Please see the documentation of that function to see which slots of the
76display table it changes. 76display table it changes.
77 77
78+++ 78---
79** 'find-function' can now find cl-defmethod invocations hidden inside macros.
79** Child frames are now supported on TTY frames. 80** Child frames are now supported on TTY frames.
80This supports use-cases like Posframe, Corfu, and child frames acting 81This supports use-cases like Posframe, Corfu, and child frames acting
81like tooltips. 82like tooltips.
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 9a7fe26eaf3..1086bf67614 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -1082,13 +1082,36 @@ MET-NAME is as returned by `cl--generic-load-hist-format'."
1082 nil t) 1082 nil t)
1083 (re-search-forward base-re nil t)))) 1083 (re-search-forward base-re nil t))))
1084 1084
1085;; WORKAROUND: This can't be a defconst due to bug#21237. 1085(defun cl--generic-search-method-make-form-matcher (met-name)
1086(defvar cl--generic-find-defgeneric-regexp "(\\(?:cl-\\)?defgeneric[ \t]+%s\\_>") 1086 (let ((name (car met-name))
1087 (qualifiers (cadr met-name))
1088 (specializers (cddr met-name)))
1089 (lambda (form)
1090 (pcase form
1091 (`(cl-generic-define-method
1092 (function ,(pred (eq name)))
1093 (quote ,(and (pred listp) m-qualifiers))
1094 (quote ,(and (pred listp) m-args))
1095 ,_call-con
1096 ,_function)
1097 (ignore-errors
1098 (let* ((m-spec-args (car (cl--generic-split-args m-args)))
1099 (m-specializers
1100 (mapcar (lambda (spec-arg)
1101 (if (eq '&context (car-safe (car spec-arg)))
1102 spec-arg (cdr spec-arg)))
1103 m-spec-args)))
1104 (and (equal qualifiers m-qualifiers)
1105 (equal specializers m-specializers)))))))))
1106
1107(defconst cl--generic-find-defgeneric-regexp "(\\(?:cl-\\)?defgeneric[ \t]+%s\\_>")
1087 1108
1088(with-eval-after-load 'find-func 1109(with-eval-after-load 'find-func
1089 (defvar find-function-regexp-alist) 1110 (defvar find-function-regexp-alist)
1090 (add-to-list 'find-function-regexp-alist 1111 (add-to-list 'find-function-regexp-alist
1091 `(cl-defmethod . ,#'cl--generic-search-method)) 1112 `(cl-defmethod
1113 . (,#'cl--generic-search-method
1114 . #'cl--generic-search-method-make-form-matcher)))
1092 (add-to-list 'find-function-regexp-alist 1115 (add-to-list 'find-function-regexp-alist
1093 '(cl-defgeneric . cl--generic-find-defgeneric-regexp))) 1116 '(cl-defgeneric . cl--generic-find-defgeneric-regexp)))
1094 1117
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 455095c9be6..c2101617ac3 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -144,6 +144,16 @@ Instead of regexp variable, types can be mapped to functions as well,
144in which case the function is called with one argument (the object 144in which case the function is called with one argument (the object
145we're looking for) and it should search for it. 145we're looking for) and it should search for it.
146 146
147A value can also be a cons (REGEX . EXPANDED-FORM-MATCHER-FACTORY).
148REGEX is as above; EXPANDED-FORM-MATCHER-FACTORY is a function of one
149argument, the same as we'd pass to a REGEX function, that returns
150another function of one argument that returns true if we're looking at a
151macroexpanded form that defines what we're looking for. If you want to
152use EXPANDED-FORM-MATCHER-FACTORY exclusively, you can set REGEX to a
153never-match regex and force the fallback to
154EXPANDED-FORM-MATCHER-FACTORY. The buffer to search is current during
155the call to EXPANDED-FORM-MATCHER-FACTORY.
156
147Symbols can have their own version of this alist on 157Symbols can have their own version of this alist on
148the property `find-function-type-alist'. 158the property `find-function-type-alist'.
149See the function `find-function-update-type-alist'.") 159See the function `find-function-update-type-alist'.")
@@ -434,7 +444,13 @@ The search is done in the source for library LIBRARY."
434 (regexp-symbol 444 (regexp-symbol
435 (or (and (symbolp symbol) 445 (or (and (symbolp symbol)
436 (alist-get type (get symbol 'find-function-type-alist))) 446 (alist-get type (get symbol 'find-function-type-alist)))
437 (alist-get type find-function-regexp-alist)))) 447 (alist-get type find-function-regexp-alist)))
448 (form-matcher-factory
449 (and (functionp (cdr-safe regexp-symbol))
450 (cdr regexp-symbol)))
451 (regexp-symbol (if form-matcher-factory
452 (car regexp-symbol)
453 regexp-symbol)))
438 (with-current-buffer (find-file-noselect filename) 454 (with-current-buffer (find-file-noselect filename)
439 (let ((regexp (if (functionp regexp-symbol) regexp-symbol 455 (let ((regexp (if (functionp regexp-symbol) regexp-symbol
440 (format (symbol-value regexp-symbol) 456 (format (symbol-value regexp-symbol)
@@ -474,7 +490,8 @@ The search is done in the source for library LIBRARY."
474 ;; expands macros until it finds the symbol. 490 ;; expands macros until it finds the symbol.
475 (cons (current-buffer) 491 (cons (current-buffer)
476 (find-function--search-by-expanding-macros 492 (find-function--search-by-expanding-macros
477 (current-buffer) symbol type)))))))))) 493 (current-buffer) symbol type
494 form-matcher-factory))))))))))
478 495
479;;;###autoload 496;;;###autoload
480(defun find-function-update-type-alist (symbol type variable) 497(defun find-function-update-type-alist (symbol type variable)
@@ -506,19 +523,13 @@ Return t if any PRED returns t."
506 (find-function--any-subform-p left-child pred) 523 (find-function--any-subform-p left-child pred)
507 (find-function--any-subform-p right-child pred)))))) 524 (find-function--any-subform-p right-child pred))))))
508 525
509(defun find-function--search-by-expanding-macros (buf symbol type) 526(defun find-function--search-by-expanding-macros
527 (buf symbol type matcher-factory)
510 "Expand macros in BUF to search for the definition of SYMBOL of TYPE." 528 "Expand macros in BUF to search for the definition of SYMBOL of TYPE."
511 (catch 'found 529 (with-current-buffer buf
512 (with-current-buffer buf 530 (when-let* ((expected-symbol-p
513 (save-excursion 531 (cond ((null type)
514 (goto-char (point-min)) 532 (lambda (form)
515 (condition-case nil
516 (while t
517 (let ((form (read (current-buffer)))
518 (expected-symbol-p
519 (lambda (form)
520 (cond
521 ((null type)
522 ;; Check if a given form is a `defalias' to 533 ;; Check if a given form is a `defalias' to
523 ;; SYM, the function name we are searching 534 ;; SYM, the function name we are searching
524 ;; for. All functions in Emacs Lisp 535 ;; for. All functions in Emacs Lisp
@@ -526,20 +537,28 @@ Return t if any PRED returns t."
526 ;; after several steps of macroexpansion. 537 ;; after several steps of macroexpansion.
527 (and (eq (car-safe form) 'defalias) 538 (and (eq (car-safe form) 'defalias)
528 (equal (car-safe (cdr form)) 539 (equal (car-safe (cdr form))
529 `(quote ,symbol)))) 540 `(quote ,symbol)))))
530 ((eq type 'defvar) 541 ((eq type 'defvar)
542 (lambda (form)
531 ;; Variables generated by macros ultimately 543 ;; Variables generated by macros ultimately
532 ;; expand to `defvar'. 544 ;; expand to `defvar'.
533 (and (eq (car-safe form) 'defvar) 545 (and (eq (car-safe form) 'defvar)
534 (eq (car-safe (cdr form)) symbol))) 546 (eq (car-safe (cdr form)) symbol))))
535 (t nil))))) 547 (matcher-factory
548 (funcall matcher-factory symbol)))))
549 (catch 'found
550 (save-excursion
551 (goto-char (point-min))
552 (condition-case nil
553 (while t
536 (when (find-function--any-subform-p 554 (when (find-function--any-subform-p
537 (find-function--try-macroexpand form) 555 (find-function--try-macroexpand
556 (read (current-buffer)))
538 expected-symbol-p) 557 expected-symbol-p)
539 ;; We want to return the location at the beginning 558 ;; We want to return the location at the beginning
540 ;; of the macro, so move back one sexp. 559 ;; of the macro, so move back one sexp.
541 (throw 'found (progn (backward-sexp) (point)))))) 560 (throw 'found (progn (backward-sexp) (point)))))
542 (end-of-file nil)))))) 561 (end-of-file nil)))))))
543 562
544(defun find-function-library (function &optional lisp-only verbose) 563(defun find-function-library (function &optional lisp-only verbose)
545 "Return the pair (ORIG-FUNCTION . LIBRARY) for FUNCTION. 564 "Return the pair (ORIG-FUNCTION . LIBRARY) for FUNCTION.