helpful

a fork of Wilfred's helpful
git clone https://git.trogloxene.org/helpful.git
Log | Files | Refs | README

commit 518d24b76bc6e4d55c8b2c288a33faf67100b101
parent a7f762fa50dd1bbac9c546599f89e3bb037f43c7
Author: Wilfred Hughes <me@wilfred.me.uk>
Date:   Wed, 13 Dec 2017 23:26:46 +0000

Ensure that we cleanup freshly created buffers

This fixes the failing tests. It also ensures that we show the correct
source code for primitive functions/special forms.

We also use (-let ((foo nil)) ...) rather than (-let ((foo)) ...)
or (-let (foo) ...) to work around an edebug issue:
https://github.com/magnars/dash.el/issues/256

Diffstat:
Mhelpful.el | 178++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 100 insertions(+), 78 deletions(-)

diff --git a/helpful.el b/helpful.el @@ -182,25 +182,31 @@ This allows us to distinguish strings from symbols." (not (helpful--primitive-p sym callable-p)) ;; We need to be able to find its definition, or we can't step ;; through the source. - (-when-let ((buf . pos) (helpful--definition sym t)) + (-when-let ((buf pos opened) (helpful--definition sym t)) + (when opened + (kill-buffer buf)) t))) (defun helpful--toggle-edebug (sym) "Enable edebug when function SYM is called, or disable if already enabled." - (-if-let ((buf . pos) (helpful--definition sym t)) - (with-current-buffer buf - (save-excursion - (save-restriction - (widen) - (goto-char pos) - - (let* ((should-edebug (not (helpful--edebug-p sym))) - (edebug-all-forms should-edebug) - (edebug-all-defs should-edebug) - (form (edebug-read-top-level-form))) - ;; Based on `edebug-eval-defun'. - (eval (eval-sexp-add-defvars form) lexical-binding))))) + (-if-let ((buf pos created) (helpful--definition sym t)) + (progn + (with-current-buffer buf + (save-excursion + (save-restriction + (widen) + (goto-char pos) + + (let* ((should-edebug (not (helpful--edebug-p sym))) + (edebug-all-forms should-edebug) + (edebug-all-defs should-edebug) + (form (edebug-read-top-level-form))) + ;; Based on `edebug-eval-defun'. + (eval (eval-sexp-add-defvars form) lexical-binding))))) + (when created + (kill-buffer buf))) + (user-error "Could not find source for edebug"))) (defun helpful--edebug (button) @@ -404,25 +410,24 @@ hooks.") (defun helpful--source (sym callable-p) "Return the source code of SYM. If the source code cannot be found, return the sexp used." - (-let ((initial-buffers (buffer-list)) - ((buf . start-pos) (helpful--definition sym callable-p))) - (if (and buf start-pos) - (let (source) - (with-current-buffer buf - (save-excursion - (save-restriction - (goto-char start-pos) - (narrow-to-defun) - (setq source (buffer-substring-no-properties (point-min) (point-max)))))) - ;; If we've just created this buffer, close it. - (unless (-contains-p initial-buffers buf) - (kill-buffer buf)) - (when (and source (buffer-file-name buf)) - (setq source (propertize source - 'helpful-path (buffer-file-name buf) - 'helpful-pos start-pos - 'helpful-pos-is-start t))) - source) + (-let (((buf start-pos created) (helpful--definition sym callable-p)) + (source nil)) + (when (and buf start-pos) + (with-current-buffer buf + (save-excursion + (save-restriction + (goto-char start-pos) + (narrow-to-defun) + (setq source (buffer-substring-no-properties (point-min) (point-max)))))) + (when (and source (buffer-file-name buf)) + (setq source (propertize source + 'helpful-path (buffer-file-name buf) + 'helpful-pos start-pos + 'helpful-pos-is-start t)))) + (when (and buf created) + (kill-buffer buf)) + (if source + source ;; Could not find source -- probably defined interactively, or via ;; a macro, or file has changed. ;; TODO: verify that the source hasn't changed before showing. @@ -440,65 +445,82 @@ If the source code cannot be found, return the sexp used." (assoc-string sym completions)))) (defun helpful--definition (sym callable-p) - "Return a pair (BUF . POS) where SYM is defined. + "Return a list (BUF POS OPENED) where SYM is defined. + +BUF is the buffer containing the definition. If the user wasn't +already visiting this buffer, OPENED is t and callers should kill +the buffer when done. -BUF may be an existing buffer or created. Caller is responsible -for killing the newly created buffer." - (let (buf-and-pos) +POS is the position of the start of the definition within the +buffer." + (-let ((initial-buffers (buffer-list)) + (primitive-p (helpful--primitive-p sym callable-p)) + (path nil) + (buf nil) + (pos nil) + (opened nil)) (when callable-p - ;; This is basically `find-function-noselect', but that moves - ;; point and can't find definitions if narrowing is in effect. - ;; - ;; Narrowing has been fixed upstream: - ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=abd18254aec76b26e86ae27e91d2c916ec20cc46 - (-when-let ((base-sym . path) (find-function-library sym)) - (when (string-match "\\`src/\\(.*\\.\\(c\\|m\\)\\)\\'" path) - (cl-assert find-function-C-source-directory) - (setq path (f-expand (match-string 1 path) - find-function-C-source-directory))) - - ;; Open `path' ourselves. If the user has already opened it, - ;; we ensure that we don't change their narrowing or point - ;; position when we search it. - (with-current-buffer (find-file-noselect (find-library-name path)) - (save-excursion - (save-restriction - (widen) - (setq buf-and-pos - ;; `base-sym' is the underlying symbol if `sym' is an alias. - (find-function-search-for-symbol base-sym nil path)))))) - - (unless buf-and-pos - ;; If it's defined interactively, it may have an edebug property - ;; that tells us where it's defined. - (-when-let (marker (car-safe (get sym 'edebug))) - (setq buf-and-pos - (cons (marker-buffer marker) - (marker-position marker)))))) - (when (not callable-p) + (-let [(base-sym . src-path) (find-function-library sym)] + ;; `base-sym' is the underlying symbol if `sym' is an alias. + (setq sym base-sym) + (setq path src-path))) + (when primitive-p + ;; Convert "src/foo.c" to "". + (setq path (f-expand path + (f-parent find-function-C-source-directory)))) + + (cond + ((and callable-p path) + ;; Open `path' ourselves, so we can widen before searching. + (setq buf (find-file-noselect (find-library-name path))) + + (unless (-contains-p initial-buffers buf) + (setq opened t)) + + ;; Based on `find-function-noselect'. + (with-current-buffer buf + ;; `find-function-search-for-symbol' moves point. Prevent + ;; that. + (save-excursion + ;; Narrowing has been fixed upstream: + ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=abd18254aec76b26e86ae27e91d2c916ec20cc46 + (save-restriction + (widen) + (setq pos + (if primitive-p + (cdr (find-function-C-source sym path nil)) + (cdr (find-function-search-for-symbol sym nil path)))))))) + (callable-p + ;; Functions defined interactively may have an edebug property + ;; that contains the location of the definition. + (-when-let (marker (car-safe (get sym 'edebug))) + (setq buf (marker-buffer marker)) + (setq pos (marker-position marker)))) + ((not callable-p) (condition-case _err - (setq buf-and-pos (find-definition-noselect sym 'defvar)) - (search-failed nil))) - buf-and-pos)) + (-let [(sym-buf . sym-pos) (find-definition-noselect sym 'defvar)] + (setq buf sym-buf) + (setq pos sym-pos)) + (search-failed nil)))) + (list buf pos opened))) (defun helpful--source-path (sym callable-p) "Return the path where SYM is defined." - (-let* ((initial-buffers (buffer-list)) - ((buf . _) (helpful--definition sym callable-p)) - (path)) + (-let* (((buf _ opened) (helpful--definition sym callable-p)) + (path nil)) (when buf (setq path (buffer-file-name buf)) - ;; If we've just created this buffer, close it. - (unless (-contains-p initial-buffers buf) + (when opened + ;; If we've just created this buffer, close it. (kill-buffer buf))) path)) (defun helpful--source-pos (sym callable-p) "Return the file position where SYM is defined." - (-let ((initial-buffers (buffer-list)) - ((buf . pos) (helpful--definition sym callable-p))) + (-let (((buf pos opened) (helpful--definition sym callable-p))) ;; If we've just created this buffer, close it. - (unless (-contains-p initial-buffers buf) + (when opened + ;; If we've just created this buffer, close it. (kill-buffer buf)) pos))