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.
This commit is contained in:
parent
59fd8c26be
commit
364c3dbc12
4 changed files with 83 additions and 28 deletions
|
@ -849,9 +849,16 @@ The alist @code{find-function-regexp-alist} associates object types with
|
|||
a regexp or function that finds the definition of that object in its
|
||||
source file. Each element's car is a symbol the describes the type of
|
||||
object, or @code{nil} to identify functions defined with @code{defun}.
|
||||
Each element's cdr is a symbol: either the value of that symbol is a
|
||||
string interpreted as a regexp, or that symbol names a function that can
|
||||
find the definition.
|
||||
Each element's cdr can be:
|
||||
|
||||
@itemize
|
||||
@item
|
||||
A symbol whose value is a string interpreted as a regexp
|
||||
@item
|
||||
A symbol naming a function that can find the definition
|
||||
@item
|
||||
A 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
|
||||
@end itemize
|
||||
|
||||
A regexp string is actually a format string, and @code{%s} will be
|
||||
substituted 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.
|
|||
A function will be called with one argument, the (symbol for) the object
|
||||
we are searching for.
|
||||
|
||||
The form-matcher function in a cons cell value is called with one argument (the
|
||||
symbol being sought) and should return a function that takes a form and returns
|
||||
non-nil if the form defines the sought symbol. This is useful for finding
|
||||
definitions that are created by macro expansion.
|
||||
|
||||
@cindex @code{definition-name} (symbol property)
|
||||
If the function to be found is defined by a macro, it may be hard for
|
||||
Emacs to find the definition site in the source code. A macro call may
|
||||
|
|
3
etc/NEWS
3
etc/NEWS
|
@ -75,7 +75,8 @@ the 'standard-display-table's extra slots with Unicode characters.
|
|||
Please see the documentation of that function to see which slots of the
|
||||
display table it changes.
|
||||
|
||||
+++
|
||||
---
|
||||
** 'find-function' can now find cl-defmethod invocations hidden inside macros.
|
||||
** Child frames are now supported on TTY frames.
|
||||
This supports use-cases like Posframe, Corfu, and child frames acting
|
||||
like tooltips.
|
||||
|
|
|
@ -1082,13 +1082,36 @@ MET-NAME is as returned by `cl--generic-load-hist-format'."
|
|||
nil t)
|
||||
(re-search-forward base-re nil t))))
|
||||
|
||||
;; WORKAROUND: This can't be a defconst due to bug#21237.
|
||||
(defvar cl--generic-find-defgeneric-regexp "(\\(?:cl-\\)?defgeneric[ \t]+%s\\_>")
|
||||
(defun cl--generic-search-method-make-form-matcher (met-name)
|
||||
(let ((name (car met-name))
|
||||
(qualifiers (cadr met-name))
|
||||
(specializers (cddr met-name)))
|
||||
(lambda (form)
|
||||
(pcase form
|
||||
(`(cl-generic-define-method
|
||||
(function ,(pred (eq name)))
|
||||
(quote ,(and (pred listp) m-qualifiers))
|
||||
(quote ,(and (pred listp) m-args))
|
||||
,_call-con
|
||||
,_function)
|
||||
(ignore-errors
|
||||
(let* ((m-spec-args (car (cl--generic-split-args m-args)))
|
||||
(m-specializers
|
||||
(mapcar (lambda (spec-arg)
|
||||
(if (eq '&context (car-safe (car spec-arg)))
|
||||
spec-arg (cdr spec-arg)))
|
||||
m-spec-args)))
|
||||
(and (equal qualifiers m-qualifiers)
|
||||
(equal specializers m-specializers)))))))))
|
||||
|
||||
(defconst cl--generic-find-defgeneric-regexp "(\\(?:cl-\\)?defgeneric[ \t]+%s\\_>")
|
||||
|
||||
(with-eval-after-load 'find-func
|
||||
(defvar find-function-regexp-alist)
|
||||
(add-to-list 'find-function-regexp-alist
|
||||
`(cl-defmethod . ,#'cl--generic-search-method))
|
||||
`(cl-defmethod
|
||||
. (,#'cl--generic-search-method
|
||||
. #'cl--generic-search-method-make-form-matcher)))
|
||||
(add-to-list 'find-function-regexp-alist
|
||||
'(cl-defgeneric . cl--generic-find-defgeneric-regexp)))
|
||||
|
||||
|
|
|
@ -144,6 +144,16 @@ Instead of regexp variable, types can be mapped to functions as well,
|
|||
in which case the function is called with one argument (the object
|
||||
we're looking for) and it should search for it.
|
||||
|
||||
A value can also be a cons (REGEX . EXPANDED-FORM-MATCHER-FACTORY).
|
||||
REGEX is as above; EXPANDED-FORM-MATCHER-FACTORY is a function of one
|
||||
argument, the same as we'd pass to a REGEX function, that returns
|
||||
another function of one argument that returns true if we're looking at a
|
||||
macroexpanded form that defines what we're looking for. If you want to
|
||||
use EXPANDED-FORM-MATCHER-FACTORY exclusively, you can set REGEX to a
|
||||
never-match regex and force the fallback to
|
||||
EXPANDED-FORM-MATCHER-FACTORY. The buffer to search is current during
|
||||
the call to EXPANDED-FORM-MATCHER-FACTORY.
|
||||
|
||||
Symbols can have their own version of this alist on
|
||||
the property `find-function-type-alist'.
|
||||
See the function `find-function-update-type-alist'.")
|
||||
|
@ -434,7 +444,13 @@ The search is done in the source for library LIBRARY."
|
|||
(regexp-symbol
|
||||
(or (and (symbolp symbol)
|
||||
(alist-get type (get symbol 'find-function-type-alist)))
|
||||
(alist-get type find-function-regexp-alist))))
|
||||
(alist-get type find-function-regexp-alist)))
|
||||
(form-matcher-factory
|
||||
(and (functionp (cdr-safe regexp-symbol))
|
||||
(cdr regexp-symbol)))
|
||||
(regexp-symbol (if form-matcher-factory
|
||||
(car regexp-symbol)
|
||||
regexp-symbol)))
|
||||
(with-current-buffer (find-file-noselect filename)
|
||||
(let ((regexp (if (functionp regexp-symbol) regexp-symbol
|
||||
(format (symbol-value regexp-symbol)
|
||||
|
@ -474,7 +490,8 @@ The search is done in the source for library LIBRARY."
|
|||
;; expands macros until it finds the symbol.
|
||||
(cons (current-buffer)
|
||||
(find-function--search-by-expanding-macros
|
||||
(current-buffer) symbol type))))))))))
|
||||
(current-buffer) symbol type
|
||||
form-matcher-factory))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun find-function-update-type-alist (symbol type variable)
|
||||
|
@ -506,19 +523,13 @@ Return t if any PRED returns t."
|
|||
(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)
|
||||
(defun find-function--search-by-expanding-macros
|
||||
(buf symbol type matcher-factory)
|
||||
"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)
|
||||
(with-current-buffer buf
|
||||
(when-let* ((expected-symbol-p
|
||||
(cond ((null type)
|
||||
(lambda (form)
|
||||
;; Check if a given form is a `defalias' to
|
||||
;; SYM, the function name we are searching
|
||||
;; for. All functions in Emacs Lisp
|
||||
|
@ -526,20 +537,28 @@ Return t if any PRED returns t."
|
|||
;; after several steps of macroexpansion.
|
||||
(and (eq (car-safe form) 'defalias)
|
||||
(equal (car-safe (cdr form))
|
||||
`(quote ,symbol))))
|
||||
((eq type 'defvar)
|
||||
`(quote ,symbol)))))
|
||||
((eq type 'defvar)
|
||||
(lambda (form)
|
||||
;; Variables generated by macros ultimately
|
||||
;; expand to `defvar'.
|
||||
(and (eq (car-safe form) 'defvar)
|
||||
(eq (car-safe (cdr form)) symbol)))
|
||||
(t nil)))))
|
||||
(eq (car-safe (cdr form)) symbol))))
|
||||
(matcher-factory
|
||||
(funcall matcher-factory symbol)))))
|
||||
(catch 'found
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(condition-case nil
|
||||
(while t
|
||||
(when (find-function--any-subform-p
|
||||
(find-function--try-macroexpand form)
|
||||
(find-function--try-macroexpand
|
||||
(read (current-buffer)))
|
||||
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))))))
|
||||
(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.
|
||||
|
|
Loading…
Add table
Reference in a new issue