unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Unknown <unknown@unknown.invalid>
To: Drew Adams <drew.adams@oracle.com>
Cc: rms@gnu.org, 45443@debbugs.gnu.org
Subject: bug#45443: 28.0.50; Can't find definition of compilation--message->loc
Date: Sun, 27 Dec 2020 01:51:29 +0100	[thread overview]
Message-ID: <m1o8igm5pq.fsf@yahoo.es> (raw)
In-Reply-To: <ac20d9d3-c5d8-4c0f-8e3a-be6c05db935b@default> (Drew Adams's message of "Sat, 26 Dec 2020 10:58:21 -0800 (PST)")

[-- Attachment #1: Type: text/plain, Size: 1226 bytes --]

Drew Adams <drew.adams@oracle.com> writes:

>> It's a general problem with uses of cl-defstruct and similar
>> constructs: they generate functions and macros that the Help functions
>> are unable to find.
>
> Yep.  It's a big doc/help problem, IMO.  And
> we've moved more and more stuff to things like
> `cl-defstruct' (which in itself is fine).
>
> I don't see a good general solution to this
> problem.  But it is, I think, something
> important for the quintessential
> "self-documenting" editor.  I truly hope that
> some smart and ambitious hacker tackles this.

One possible approach is, if the regular expression code fails to find a
location, we can fall back to expand macros until we find the definition
(a defalias in the case of a function, or a defvar in the case of a
variable), or we reach the end of the file.

I attach a first implementation of this approach, with some tests for
the function definition that Richard wanted to find.  I'm sure there are
still missing cases that are interesting (closures?).  Give it a try and
see if there are still relevant symbols whose definition Emacs is unable
to locate.  I'm also open to feedback about the macro expansion logic.
Could it be more efficient?  Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-find-definition-in-Help-buffers.patch --]
[-- Type: text/x-patch, Size: 6061 bytes --]

From 34662c47b359792e048bb7020d7eb0fbf94e7838 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= <mardani29@yahoo.es>
Date: Sun, 27 Dec 2020 01:07:26 +0100
Subject: [PATCH] Improve "find definition" in *Help* buffers

*
lisp/emacs-lisp/find-func.el (find-function--search-by-expanding-macros):
New internal function that searches for a function or variable by
expanding macros in a buffer.
* 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.
---
 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 a320acb5fa..2bfaccd2ef 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 074e7db295..7796a72ecf 100644
--- a/lisp/emacs-lisp/find-func.el
+++ b/lisp/emacs-lisp/find-func.el
@@ -389,7 +389,70 @@ find-function-search-for-symbol
                   (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 d77eb6757f..03df4bb9ff 100644
--- a/test/lisp/emacs-lisp/find-func-tests.el
+++ b/test/lisp/emacs-lisp/find-func-tests.el
@@ -43,5 +43,15 @@ find-func-tests--library-completion
                      (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
-- 
2.28.0


  reply	other threads:[~2020-12-27  0:51 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <<E1kt6ez-0000D4-0e@fencepost.gnu.org>
     [not found] ` <<83a6u0n8y7.fsf@gnu.org>
2020-12-26 18:58   ` bug#45443: 28.0.50; Can't find definition of compilation--message->loc Drew Adams
2020-12-27  0:51     ` Unknown [this message]
2020-12-27  8:07       ` Lars Ingebrigtsen
2020-12-27 17:40       ` Eli Zaretskii
2020-12-27 17:59         ` Andrea Corallo via Bug reports for GNU Emacs, the Swiss army knife of text editors
2020-12-27 18:05           ` Eli Zaretskii
2020-12-27 19:28           ` Unknown
2020-12-27 19:40             ` Andrea Corallo via Bug reports for GNU Emacs, the Swiss army knife of text editors
2020-12-27 19:28           ` Unknown
2020-12-26 10:18 Richard Stallman
2020-12-26 10:44 ` Eli Zaretskii
2020-12-27  5:38   ` Richard Stallman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m1o8igm5pq.fsf@yahoo.es \
    --to=unknown@unknown.invalid \
    --cc=45443@debbugs.gnu.org \
    --cc=drew.adams@oracle.com \
    --cc=rms@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).