emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 80420fa: Improve "find definition" in *Help* buffers


From: Lars Ingebrigtsen
Subject: master 80420fa: Improve "find definition" in *Help* buffers
Date: Sun, 27 Dec 2020 03:05:09 -0500 (EST)

branch: master
commit 80420faf4921ffe5e9d4c4f9595941acf3156e50
Author: Daniel Martín <mardani29@yahoo.es>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    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).
---
 etc/NEWS                                |  6 +++
 lisp/emacs-lisp/find-func.el            | 65 ++++++++++++++++++++++++++++++++-
 test/lisp/emacs-lisp/find-func-tests.el | 10 +++++
 3 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/etc/NEWS b/etc/NEWS
index 9ae8cc9..4f072df 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -114,6 +114,12 @@ choosing a group, or clicking a button in the "*Help*" 
buffers when
 looking at the doc string of a function that belongs to one of these
 groups.
 
+---
+** Improved "find definition" feature of *Help* buffers.
+Now clicking on the link to find the definition of functions generated
+by 'cl-defstruct', or variables generated by 'define-derived-mode',
+for example, will go to the exact place where they are defined.
+
 ** New variable 'redisplay-skip-initial-frame' to enable batch redisplay tests.
 Setting it to nil forces the redisplay to do its job even in the
 initial frame used in batch mode.
diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el
index 074e7db..7796a72 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.
diff --git a/test/lisp/emacs-lisp/find-func-tests.el 
b/test/lisp/emacs-lisp/find-func-tests.el
index d77eb67..03df4bb 100644
--- a/test/lisp/emacs-lisp/find-func-tests.el
+++ b/test/lisp/emacs-lisp/find-func-tests.el
@@ -43,5 +43,15 @@
                      (concat data-directory (kbd "n x / TAB RET"))
                    (read-library-name)))))
 
+;; Avoid a byte-compilation warning that may confuse people reading
+;; the result of the following test.
+(declare-function compilation--message->loc nil "compile")
+
+(ert-deftest find-func-tests--locate-macro-generated-symbols () ;bug#45443
+  (should (cdr (find-function-search-for-symbol
+                #'compilation--message->loc nil "compile")))
+  (should (cdr (find-function-search-for-symbol
+                'c-mode-hook 'defvar "cc-mode"))))
+
 (provide 'find-func-tests)
 ;;; find-func-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]