;; Copyright (C) 2015 Free Software Foundation, Inc.
-;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
-;; URL: http://github.com/Bruce-Connor/elisp-bug-hunter
-;; Version: 0.1
+;; Author: Artur Malabarba <emacs@endlessparentheses.com>
+;; URL: http://github.com/Malabarba/elisp-bug-hunter
+;; Version: 0.2
;; Keywords: lisp
;; Package-Requires: ((seq "1.3") (cl-lib "0.5"))
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
-;; `bug-hunter' is an Emacs library that finds the source of an error or
-;; unexpected behavior inside an elisp configuration file (tipically
+;;
+;; The Bug Hunter is an Emacs library that finds the source of an error or
+;; unexpected behavior inside an elisp configuration file (typically
;; `init.el' or `.emacs').
-;;
-;;
+;;
;; Usage Examples
;; ==============
-;;
+;;
;; If your Emacs init file signals an error during startup, but you don’t
;; know why, simply issue
;; ,----
;; | M-x bug-hunter-init-file RET RET
;; `----
-;; and `bug-hunter' will find it for you.
-;;
+;; and The Bug Hunter will find it for you. Note that your `init.el' (or
+;; `.emacs') must be idempotent for this to work.
+;;
;; If Emacs starts up without errors but something is not working as it
;; should, invoke the same command, but give it in an assertion.
;; Essentially, if you can write a snippet that detects the issue and
;; returns non-nil, just provide this snippet as the assertion and the
;; Bug Hunter will do a bisection search for you.
-;;
+;;
;; For example, let’s say there’s something in your init file that’s
;; loading the `cl' library, and you don’t want that. You /know/ you’re
;; not loading it yourself, but how can you figure out which external
;; package is responsible for this outrage?
-;;
+;;
;; ,----
;; | M-x bug-hunter-init-file RET (featurep 'cl) RET
;; `----
-;;
+;;
;; *That’s it!* You’ll be given a nice buffer reporting the results:
-;;
+;;
;; - Are you getting obscure errors when trying to open /“.tex”/ files?
-;; Don’t despair! Just use `(find-file "dummy.tex")' as the assertion.
-;; - Did `ox-html' stop working due to some arcane misconfiguration? Just
-;; write an assertion that does an export and checks the result.
+;; - Don’t despair! Just use `(find-file "dummy.tex")' as the
+;; assertion.
+;; - Did `ox-html' stop working due to some arcane misconfiguration?
+;; - Just write an assertion that does an export and checks the result.
;; - Does some random command suddenly bind itself to `C-j' and you can’t
-;; figure out why? `(eq (key-binding "\n") 'unwanted-command)' is the
-;; assertion for you!
-;;
+;; figure out why?
+;; - `(eq (key-binding "\n") 'unwanted-command)' is the assertion for
+;; you!
+;;
;; Finally, you can also use `bug-hunter-file' to hunt in other files.
-;;
-
-;; Installation
-;; ============
-;;
-;; Bug Hunter will be on Gelpa shortly. For now, do the following:
-;; 1. Open the `bug-hunter.el` file.
-;; 2. Issue `M-x package-install-from-buffer`.
\f
;;; Code:
(with-temp-file file-name
(print (list 'prin1 form) (current-buffer)))
(call-process exec nil out-buf nil
- "-Q" "--batch" "-l"
- (shell-quote-argument file-name))
+ "-Q" "--batch" "-l" file-name)
(with-current-buffer out-buf
(goto-char (point-max))
(forward-sexp -1)
SAFE is a list of forms confirmed to not match the ASSERTION,
HEAD is a list of forms to be tested now, and TAIL is a list
which will be inspected if HEAD doesn't match ASSERTION."
- (cond
- ((not tail)
- (vector (length safe)
- ;; Sometimes we already ran this, sometimes not. So it's
- ;; easier to just run it anyway to get the return value.
- (bug-hunter--run-and-test (append safe head) assertion)))
- ((and (message "Testing: %s/%s"
- (cl-incf bug-hunter--i)
- bug-hunter--estimate)
- (setq bug-hunter--current-head head)
- (bug-hunter--run-and-test (append safe head) assertion))
- (apply #'bug-hunter--bisect
- assertion
- safe
- (bug-hunter--split head)))
- (t (apply #'bug-hunter--bisect
+ (message "Testing: %s/%s" (cl-incf bug-hunter--i) bug-hunter--estimate)
+ ;; Used if the user quits.
+ (setq bug-hunter--current-head head)
+ (let ((ret-val (bug-hunter--run-and-test (append safe head) assertion)))
+ (cond
+ ((not tail)
+ (cl-assert ret-val nil)
+ (vector (length safe) ret-val))
+ ;; Issue in the head.
+ ((and ret-val (< (length head) 2))
+ (vector (length safe) ret-val))
+ (ret-val
+ (apply #'bug-hunter--bisect
assertion
- (append safe head)
- (bug-hunter--split tail)))))
+ safe
+ (bug-hunter--split head)))
+ ;; Issue in the tail.
+ (t (apply #'bug-hunter--bisect
+ assertion
+ (append safe head)
+ ;; If tail has length 1, we already know where the issue is,
+ ;; but we still do this to get the return value.
+ (bug-hunter--split tail))))))
(defun bug-hunter--bisect-start (forms assertion)
"Run a bisection search on list of FORMS using ASSERTION.
(let ((minibuffer-completing-symbol t))
(minibuffer-with-setup-hook
(lambda ()
- ;; FIXME: call emacs-lisp-mode?
- (add-function :before-until (local 'eldoc-documentation-function)
- #'elisp-eldoc-documentation-function)
(add-hook 'completion-at-point-functions
#'elisp-completion-at-point nil t)
(run-hooks 'eval-expression-minibuffer-setup-hook))