diff options
| author | Wilfred Hughes <me@wilfred.me.uk> | 2019-02-27 09:54:47 +0000 |
|---|---|---|
| committer | Wilfred Hughes <me@wilfred.me.uk> | 2019-02-27 09:54:47 +0000 |
| commit | 5058dda83c22b490120977368edd3ac64d463bc9 (patch) | |
| tree | f61143c7f74be0fcc5daa1236259d4e15745e8a7 | |
| parent | 4cf4381aca731db2f9473cc39c64413ddedcde63 (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.md | 3 | ||||
| -rw-r--r-- | helpful.el | 60 | ||||
| -rw-r--r-- | test/helpful-unit-test.el | 7 |
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. @@ -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)) |
