aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilfred Hughes <me@wilfred.me.uk>2019-02-27 09:54:47 +0000
committerWilfred Hughes <me@wilfred.me.uk>2019-02-27 09:54:47 +0000
commit5058dda83c22b490120977368edd3ac64d463bc9 (patch)
treef61143c7f74be0fcc5daa1236259d4e15745e8a7
parent4cf4381aca731db2f9473cc39c64413ddedcde63 (diff)
Better source detection for functions defined by macros
If we can't find the location but know the buffer, try expanding forms in the buffer to see if we can find the relevant function. This is largely code from elisp-def, but it wasn't in a terribly reusable form and I've copied it.
-rw-r--r--CHANGELOG.md3
-rw-r--r--helpful.el60
-rw-r--r--test/helpful-unit-test.el7
3 files changed, 69 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d6a542..3188861 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@ Fixed an issue when viewing `inhibit-read-only`.
Improved the buffer prompt to be more relevant when inspecting
buffer-local values.
+Better source detection for functions defined by `defstruct` or other
+macros.
+
# v0.15
Fixed a crash on formatting values.
diff --git a/helpful.el b/helpful.el
index 739a3fd..60f4994 100644
--- a/helpful.el
+++ b/helpful.el
@@ -1206,6 +1206,59 @@ If .elc files exist without the corresponding .el, return nil."
(find-library-name library-name)
(error nil)))
+(defun helpful--macroexpand-try (form)
+ "Try to fully macroexpand FORM.
+If it fails, attempt to partially macroexpand FORM."
+ (catch 'result
+ (ignore-errors
+ ;; Happy path: we can fully expand the form.
+ (throw 'result (macroexpand-all form)))
+ (ignore-errors
+ ;; Attempt one level of macroexpansion.
+ (throw 'result (macroexpand-1 form)))
+ ;; Fallback: just return the original form.
+ form))
+
+(defun helpful--tree-any-p (pred tree)
+ "Walk TREE, applying PRED to every subtree.
+Return t if PRED ever returns t."
+ (cond
+ ((null tree) nil)
+ ((funcall pred tree) t)
+ ((not (consp tree)) nil)
+ (t (or
+ (helpful--tree-any-p pred (car tree))
+ (helpful--tree-any-p pred (cdr tree))))))
+
+(defun helpful--find-by-macroexpanding (buf sym callable-p)
+ "Search BUF for the definition of SYM by macroexpanding
+interesting forms in BUF."
+ (catch 'found
+ (with-current-buffer buf
+ (save-excursion
+ (goto-char (point-min))
+ (condition-case nil
+ (while t
+ (let ((form (read (current-buffer)))
+ (var-def-p
+ (lambda (sexp)
+ (and (eq (car-safe sexp) 'defvar)
+ (eq (car-safe (cdr sexp)) sym))))
+ (fn-def-p
+ (lambda (sexp)
+ ;; `defun' ultimately expands to `defalias'.
+ (and (eq (car-safe sexp) 'defalias)
+ (equal (car-safe (cdr sexp)) `(quote ,sym))))))
+ (setq form (helpful--macroexpand-try form))
+
+ (when (helpful--tree-any-p
+ (if callable-p fn-def-p var-def-p)
+ form)
+ ;; `read' puts point at the end of the form, so go
+ ;; back to the start.
+ (throw 'found (scan-sexps (point) -1)))))
+ (end-of-file nil))))))
+
(defun helpful--definition (sym callable-p)
"Return a list (BUF POS OPENED) where SYM is defined.
@@ -1271,7 +1324,12 @@ buffer."
(save-restriction
(widen)
(setq pos
- (cdr (find-function-search-for-symbol sym nil library-name))))))))
+ (cdr (find-function-search-for-symbol sym nil library-name))))))
+ ;; If we found the containing buffer, but not the symbol, attempt
+ ;; to find it by macroexpanding interesting forms.
+ (when (and buf (not pos))
+ (setq pos (helpful--find-by-macroexpanding buf sym t)))))
+ ;; A function, but no file found.
(callable-p
;; Functions defined interactively may have an edebug property
;; that contains the location of the definition.
diff --git a/test/helpful-unit-test.el b/test/helpful-unit-test.el
index 756bc76..541639c 100644
--- a/test/helpful-unit-test.el
+++ b/test/helpful-unit-test.el
@@ -352,6 +352,13 @@ variables defined without `defvar'."
(-let [(buf pos opened) (helpful--definition 'test-foo-edebug-defn t)]
(should buf))))))
+(ert-deftest helpful--definition-defstruct ()
+ "Ensure we find the position of struct functions."
+ (-let [(buf pos _)
+ (helpful--definition #'make-ert-test t)]
+ (should buf)
+ (should pos)))
+
(ert-deftest helpful-variable ()
"Smoke test for `helpful-variable'."
(helpful-variable 'tab-width))