diff options
| author | Daniel Colascione | 2025-03-27 16:04:51 -0400 |
|---|---|---|
| committer | Daniel Colascione | 2025-03-27 16:21:13 -0400 |
| commit | 364c3dbc12e7b6d41ab449dd495c96d08874310e (patch) | |
| tree | 33b6c05a49a9f71b12ee4096afa78f5c067e4371 | |
| parent | 59fd8c26be2f7724a5c8cb583383f2025171c660 (diff) | |
| download | emacs-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.texi | 18 | ||||
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-generic.el | 29 | ||||
| -rw-r--r-- | lisp/emacs-lisp/find-func.el | 61 |
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 | |||
| 849 | a regexp or function that finds the definition of that object in its | 849 | a regexp or function that finds the definition of that object in its |
| 850 | source file. Each element's car is a symbol the describes the type of | 850 | source file. Each element's car is a symbol the describes the type of |
| 851 | object, or @code{nil} to identify functions defined with @code{defun}. | 851 | object, or @code{nil} to identify functions defined with @code{defun}. |
| 852 | Each element's cdr is a symbol: either the value of that symbol is a | 852 | Each element's cdr can be: |
| 853 | string interpreted as a regexp, or that symbol names a function that can | 853 | |
| 854 | find the definition. | 854 | @itemize |
| 855 | @item | ||
| 856 | A symbol whose value is a string interpreted as a regexp | ||
| 857 | @item | ||
| 858 | A symbol naming a function that can find the definition | ||
| 859 | @item | ||
| 860 | 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 | ||
| 861 | @end itemize | ||
| 855 | 862 | ||
| 856 | A regexp string is actually a format string, and @code{%s} will be | 863 | A regexp string is actually a format string, and @code{%s} will be |
| 857 | substituted with the name of the symbol we are looking for. | 864 | 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. | |||
| 859 | A function will be called with one argument, the (symbol for) the object | 866 | A function will be called with one argument, the (symbol for) the object |
| 860 | we are searching for. | 867 | we are searching for. |
| 861 | 868 | ||
| 869 | The form-matcher function in a cons cell value is called with one argument (the | ||
| 870 | symbol being sought) and should return a function that takes a form and returns | ||
| 871 | non-nil if the form defines the sought symbol. This is useful for finding | ||
| 872 | definitions that are created by macro expansion. | ||
| 873 | |||
| 862 | @cindex @code{definition-name} (symbol property) | 874 | @cindex @code{definition-name} (symbol property) |
| 863 | If the function to be found is defined by a macro, it may be hard for | 875 | If the function to be found is defined by a macro, it may be hard for |
| 864 | Emacs to find the definition site in the source code. A macro call may | 876 | Emacs to find the definition site in the source code. A macro call may |
| @@ -75,7 +75,8 @@ the 'standard-display-table's extra slots with Unicode characters. | |||
| 75 | Please see the documentation of that function to see which slots of the | 75 | Please see the documentation of that function to see which slots of the |
| 76 | display table it changes. | 76 | display 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. |
| 80 | This supports use-cases like Posframe, Corfu, and child frames acting | 81 | This supports use-cases like Posframe, Corfu, and child frames acting |
| 81 | like tooltips. | 82 | like 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, | |||
| 144 | in which case the function is called with one argument (the object | 144 | in which case the function is called with one argument (the object |
| 145 | we're looking for) and it should search for it. | 145 | we're looking for) and it should search for it. |
| 146 | 146 | ||
| 147 | A value can also be a cons (REGEX . EXPANDED-FORM-MATCHER-FACTORY). | ||
| 148 | REGEX is as above; EXPANDED-FORM-MATCHER-FACTORY is a function of one | ||
| 149 | argument, the same as we'd pass to a REGEX function, that returns | ||
| 150 | another function of one argument that returns true if we're looking at a | ||
| 151 | macroexpanded form that defines what we're looking for. If you want to | ||
| 152 | use EXPANDED-FORM-MATCHER-FACTORY exclusively, you can set REGEX to a | ||
| 153 | never-match regex and force the fallback to | ||
| 154 | EXPANDED-FORM-MATCHER-FACTORY. The buffer to search is current during | ||
| 155 | the call to EXPANDED-FORM-MATCHER-FACTORY. | ||
| 156 | |||
| 147 | Symbols can have their own version of this alist on | 157 | Symbols can have their own version of this alist on |
| 148 | the property `find-function-type-alist'. | 158 | the property `find-function-type-alist'. |
| 149 | See the function `find-function-update-type-alist'.") | 159 | See 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. |