From e24376ec5c7a8ff558ae759c27fe49fc6a283e70 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Tue, 23 Jan 2018 15:16:29 +0100 Subject: [PATCH] Update bug-hunter Signed-off-by: Andreas Gerler --- elpa/bug-hunter-1.1/bug-hunter-autoloads.el | 15 - elpa/bug-hunter-1.1/bug-hunter-pkg.el | 1 - elpa/bug-hunter-1.1/bug-hunter.el | 0 elpa/bug-hunter-1.3.1/bug-hunter-autoloads.el | 32 + elpa/bug-hunter-1.3.1/bug-hunter-pkg.el | 2 + elpa/bug-hunter-1.3.1/bug-hunter.el | 591 ++++++++++++++++++ elpa/bug-hunter-readme.txt | 92 +++ 7 files changed, 717 insertions(+), 16 deletions(-) delete mode 100644 elpa/bug-hunter-1.1/bug-hunter-autoloads.el delete mode 100644 elpa/bug-hunter-1.1/bug-hunter-pkg.el delete mode 100644 elpa/bug-hunter-1.1/bug-hunter.el create mode 100644 elpa/bug-hunter-1.3.1/bug-hunter-autoloads.el create mode 100644 elpa/bug-hunter-1.3.1/bug-hunter-pkg.el create mode 100644 elpa/bug-hunter-1.3.1/bug-hunter.el diff --git a/elpa/bug-hunter-1.1/bug-hunter-autoloads.el b/elpa/bug-hunter-1.1/bug-hunter-autoloads.el deleted file mode 100644 index f38c3f5c..00000000 --- a/elpa/bug-hunter-1.1/bug-hunter-autoloads.el +++ /dev/null @@ -1,15 +0,0 @@ -;;; bug-hunter-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) - -;;;### (autoloads nil nil ("bug-hunter.el") (22215 9170 116062 0)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; bug-hunter-autoloads.el ends here diff --git a/elpa/bug-hunter-1.1/bug-hunter-pkg.el b/elpa/bug-hunter-1.1/bug-hunter-pkg.el deleted file mode 100644 index 57950407..00000000 --- a/elpa/bug-hunter-1.1/bug-hunter-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "bug-hunter" "1.1" "Hunt down errors by bisecting elisp files" '((seq "1.3") (cl-lib "0.5")) :url "https://github.com/Malabarba/elisp-bug-hunter" :keywords '("lisp")) diff --git a/elpa/bug-hunter-1.1/bug-hunter.el b/elpa/bug-hunter-1.1/bug-hunter.el deleted file mode 100644 index e69de29b..00000000 diff --git a/elpa/bug-hunter-1.3.1/bug-hunter-autoloads.el b/elpa/bug-hunter-1.3.1/bug-hunter-autoloads.el new file mode 100644 index 00000000..24044f1a --- /dev/null +++ b/elpa/bug-hunter-1.3.1/bug-hunter-autoloads.el @@ -0,0 +1,32 @@ +;;; bug-hunter-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) + +;;;### (autoloads nil "bug-hunter" "bug-hunter.el" (23143 17261 427534 +;;;;;; 762000)) +;;; Generated autoloads from bug-hunter.el + +(autoload 'bug-hunter-file "bug-hunter" "\ +Bisect FILE while testing ASSERTION. +All sexps in FILE are read and passed to `bug-hunter-hunt' as a +list. See `bug-hunter-hunt' for how to use ASSERTION. + +\(fn FILE &optional ASSERTION)" t nil) + +(autoload 'bug-hunter-init-file "bug-hunter" "\ +Test ASSERTION throughout `user-init-file'. +All sexps inside `user-init-file' are read and passed to +`bug-hunter-hunt' as a list. See `bug-hunter-hunt' for how to use +ASSERTION. + +\(fn &optional ASSERTION)" t nil) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; bug-hunter-autoloads.el ends here diff --git a/elpa/bug-hunter-1.3.1/bug-hunter-pkg.el b/elpa/bug-hunter-1.3.1/bug-hunter-pkg.el new file mode 100644 index 00000000..959b7d82 --- /dev/null +++ b/elpa/bug-hunter-1.3.1/bug-hunter-pkg.el @@ -0,0 +1,2 @@ +;;; -*- no-byte-compile: t -*- +(define-package "bug-hunter" "1.3.1" "Hunt down errors by bisecting elisp files" '((seq "1.3") (cl-lib "0.5")) :url "https://github.com/Malabarba/elisp-bug-hunter" :keywords '("lisp")) diff --git a/elpa/bug-hunter-1.3.1/bug-hunter.el b/elpa/bug-hunter-1.3.1/bug-hunter.el new file mode 100644 index 00000000..9406a0a2 --- /dev/null +++ b/elpa/bug-hunter-1.3.1/bug-hunter.el @@ -0,0 +1,591 @@ +;;; bug-hunter.el --- Hunt down errors by bisecting elisp files -*- lexical-binding: t; -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Artur Malabarba +;; URL: https://github.com/Malabarba/elisp-bug-hunter +;; Version: 1.3.1 +;; Keywords: lisp +;; Package-Requires: ((seq "1.3") (cl-lib "0.5")) + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; 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 +;; ============== +;; +;; Automated error hunting +;; ~~~~~~~~~~~~~~~~~~~~~~~ +;; +;; 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 e +;; `---- +;; and The Bug Hunter will find it for you. Note that your `init.el' (or +;; `.emacs') must be idempotent for this to work. +;; +;; +;; Interactive hunt +;; ~~~~~~~~~~~~~~~~ +;; +;; If Emacs starts up without errors but something is not working as it +;; should, invoke the same command, but choose the interactive option: +;; ,---- +;; | M-x bug-hunter-init-file RET i +;; `---- +;; The Bug Hunter will start a separate Emacs instance several times, and +;; then it will ask you each time whether that instance presented the +;; problem you have. After doing this about 5--12 times, you’ll be given +;; the results. +;; +;; +;; Assertion hunt +;; ~~~~~~~~~~~~~~ +;; +;; The Bug Hunter can also find your issue based on an assertion. +;; Essentially, if you can write a code snippet that returns non-nil when +;; it detects the issue, just provide this snippet as the assertion and +;; the Bug Hunter will do the rest. +;; +;; 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 a (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. +;; - 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! +;; +;; Finally, you can also use `bug-hunter-file' to hunt in other files. +;; +;; +;; init.org and other literate-style configs +;; ========================================= +;; +;; Please see the full Readme on http://github.com/Malabarba/elisp-bug-hunter + +;;; Code: +(require 'seq) +(require 'cl-lib) + +(defconst bug-hunter--interactive-explanation + "You have asked to do an interactive hunt, here's how it goes. +1) I will start a new Emacs instance, which opens a new frame. +2) You will try to reproduce your problem on the new frame. +3) When you’re done, close that frame. +4) I will ask you if you managed to reproduce the problem. +5) We will repeat steps up to %s times, so hang tight!") + +(defconst bug-hunter--assertion-reminder + "Remember, the assertion must be an expression that returns +non-nil in your current (problematic) Emacs state, AND that +returns nil on a clean Emacs instance. +If you're unsure how to write an assertion, you can try the interactive +hunt instead, or see some examples in the Readme: + https://github.com/Malabarba/elisp-bug-hunter" + "Printed to the user if they provide a bad assertion.") + +(defvar bug-hunter--current-head nil + "Current list of expressions under scrutiny. Used for user feedback. +Used if the user aborts before bisection ends.") + +(defvar bug-hunter--i 0 + "Current step of the bisection. Used for user feedback.") +(defvar bug-hunter--estimate 0 + "Estimate on how many steps the bisection can take. Used for user feedback. +This is the base 2 log of the number of expressions in the +file.") + +(defvar bug-hunter--current-file nil + "File currently being debugged.") + +(defun bug-hunter--read-buffer () + "Return all sexps after point as a list." + (let (out line col) + (or (condition-case er + ;; Looks hacky, but comes from `byte-compile-from-buffer'. + (while (progn (while (progn (skip-chars-forward " \t\n\^l") + (looking-at ";")) + (forward-line 1)) + (not (eobp))) + (setq line (line-number-at-pos (point))) + (setq col (current-column)) + (push (list (read (current-buffer)) line col) + out) + nil) + (end-of-file `(bug-caught (end-of-file) ,line ,col)) + (invalid-read-syntax `(bug-caught ,er ,line ,col)) + (error + (if (string= (elt er 1) "Invalid modifier in string") + `(bug-caught (invalid-modifier) ,line ,col) + (error "Ran into an error we don't understand, please file a bug report: %S" er)))) + (nreverse out)))) + +(defun bug-hunter--read-contents (file) + "Return all sexps in FILE as a list." + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (bug-hunter--read-buffer))) + + +;;; Reporting functions +(defun bug-hunter--report-print (&rest r) + "Print information on the \"*Bug-Hunter Report*\" buffer. +R is passed to `format' and inserted." + (with-current-buffer (get-buffer-create "*Bug-Hunter Report*") + (goto-char (point-max)) + (let ((inhibit-read-only t)) + (insert "\n" (apply #'format r))))) + +(defun bug-hunter--report (&rest r) + "Report arbitrary information. +R is passed to `bug-hunter--report-print'." + (declare (indent 1)) + (apply #'bug-hunter--report-print r) + (redisplay) + (apply #'message r)) + +(defun bug-hunter--report-user-error (&rest r) + "Report the user has done something wrong. +R is passed to `bug-hunter--report-print'." + (declare (indent 1)) + (apply #'bug-hunter--report-print r) + (bug-hunter--report-print "\xc\n") + (apply #'user-error r)) + +(defvar compilation-error-regexp-alist) +(defun bug-hunter--init-report-buffer (assertion steps) + "Create and prepare the \"*Bug-Hunter Report*\" buffer. +Also add some descriptions depending on ASSERTION." + (with-current-buffer (get-buffer-create "*Bug-Hunter Report*") + (let ((inhibit-read-only t)) + (erase-buffer) + (compilation-mode "Bug Hunt") + (set (make-local-variable 'compilation-error-regexp-alist) + '(comma)) + (pcase assertion + (`interactive (insert (format bug-hunter--interactive-explanation (+ 2 steps)))))) + (current-buffer))) + +(defun bug-hunter--pretty-format (value padding) + "Return a VALUE as a string with PADDING spaces on the left." + (with-temp-buffer + (pp value (current-buffer)) + (goto-char (point-min)) + (let ((pad (make-string padding ?\s))) + (while (not (eobp)) + (insert pad) + (forward-line 1))) + (buffer-string))) + +(defun bug-hunter--report-error (error line column &optional expression) + "Print on report buffer information about ERROR. +LINE and COLUMN are the coordinates where EXPRESSION started in +the file." + (when line + (bug-hunter--report "%S, line %s pos %s:" + bug-hunter--current-file line column)) + (bug-hunter--report " %s" + (cl-case (car error) + (end-of-file + "There's a missing closing parenthesis, the expression on this line never ends.") + (invalid-modifier (concat "There's a string on this line with an invalid modifier." + "\n A \"modifier\" is a \\ followed by a few characters." + "\n For example, \\C-; is an invalid modifier.")) + (invalid-read-syntax + (let ((char (cadr error))) + (if (member char '("]" ")")) + (concat "There's an extra " char + " on this position. There's probably a missing " + (if (string= char ")") "(" "[") + " before that.") + (concat "There's a " char + " on this position, and that is not valid elisp syntax.")))) + (user-aborted + (let* ((print-level 2) + (print-length 15) + (forms (cadr error)) + (size (length forms))) + (concat "User aborted while testing the following expressions:\n" + (mapconcat (lambda (x) (bug-hunter--pretty-format x 4)) + (if (< size 16) forms (seq-take forms 7)) + "") + (when (> size 16) + (format "\n ... %s omitted expressions ...\n\n" + (- size 14))) + (when (> size 16) + (mapconcat (lambda (x) (bug-hunter--pretty-format x 4)) + (seq-drop forms (- size 7)) ""))))) + (assertion-triggered + (concat "The assertion returned the following value here:\n" + (bug-hunter--pretty-format (cadr error) 4))) + (t (format "The following error was signaled here:\n %S" + error)))) + (when expression + (bug-hunter--report " Caused by the following expression:\n%s" + (bug-hunter--pretty-format expression 4))) + (bug-hunter--report "\xc\n") + `[,error ,line ,column ,expression]) + + +;;; Execution functions +(defun bug-hunter--print-to-temp (sexp) + "Print SEXP to a temp file and return the file name." + (let ((print-length nil) + (print-level nil) + (file (make-temp-file "bug-hunter"))) + (with-temp-file file + (print sexp (current-buffer))) + file)) + +(defun bug-hunter--run-emacs (file &rest args) + "Start an Emacs process to run FILE and return the output buffer. +ARGS are passed before \"-l FILE\"." + (let ((out-buf (generate-new-buffer "*Bug-Hunter Command*")) + (exec (file-truename (expand-file-name invocation-name + invocation-directory)))) + (apply #'call-process exec nil out-buf nil + (append args (list "-l" file))) + out-buf)) + +(defun bug-hunter--run-form (form) + "Run FORM with \"emacs -Q\" and return the result." + (let ((file-name (bug-hunter--print-to-temp (list 'prin1 form)))) + (unwind-protect + (with-current-buffer (bug-hunter--run-emacs file-name "-Q" "--batch") + (goto-char (point-max)) + (forward-sexp -1) + (prog1 (read (current-buffer)) + (kill-buffer (current-buffer)))) + (delete-file file-name)))) + +(defun bug-hunter--run-form-interactively (form) + "Run FORM in a graphical instance and ask user about the outcome." + (let ((file-name (bug-hunter--print-to-temp (list 'prin1 form)))) + (unwind-protect + (bug-hunter--run-emacs file-name "-Q") + (delete-file file-name)) + (y-or-n-p "Did you find the problem/bug in this instance (if you encounter some other issue, answer `n')? "))) + +(defun bug-hunter--wrap-forms-for-eval (forms) + "Return FORMS wrapped in initialization code." + `(let ((server-name (make-temp-file "bug-hunter-temp-server-file"))) + (delete-file server-name) + (if site-run-file (load site-run-file t t)) + (run-hooks 'before-init-hook) + ,@forms + (package-initialize) + (run-hooks 'after-init-hook))) + +(defun bug-hunter--run-and-test (forms assertion) + "Execute FORMS in the background and test ASSERTION. +See `bug-hunter' for a description on the ASSERTION. + +If ASSERTION is 'interactive, the form is run through +`bug-hunter--run-form-interactively'. Otherwise, a slightly +modified version of the form combined with ASSERTION is run +through `bug-hunter--run-form'." + (if (eq assertion 'interactive) + (bug-hunter--run-form-interactively + (bug-hunter--wrap-forms-for-eval forms)) + (bug-hunter--run-form + `(condition-case er + ,(append (bug-hunter--wrap-forms-for-eval forms) + (list assertion)) + (error (cons 'bug-caught er)))))) + + + +;;; The actual bisection +(defun bug-hunter--split (l) + "Split list L in two lists of same size." + (seq-partition l (ceiling (/ (length l) 2.0)))) + +(defun bug-hunter--bisect (assertion safe head &optional tail) + "Implementation used by `bug-hunter--bisect-start'. +ASSERTION is received by `bug-hunter--bisect-start'. +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." + (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 + 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. +Returns a vector [n value], where n is the position of the first +element in FORMS which trigger ASSERTION, and value is the +ASSERTION's return value. + +If ASSERTION is nil, n is the position of the first form to +signal an error and value is (bug-caught . ERROR-SIGNALED)." + (let ((bug-hunter--i 0) + (bug-hunter--current-head nil)) + (condition-case-unless-debug nil + (apply #'bug-hunter--bisect assertion nil (bug-hunter--split forms)) + (quit `[nil (bug-caught user-aborted ,bug-hunter--current-head)])))) + + +;;; Main functions +(defun bug-hunter-hunt (rich-forms assertion) + "Bisect RICH-FORMS using ASSERTION. +RICH-FORMS is a list with elements of the form: (EXPR LINE COL) + EXPR is an elisp expression. LINE and COL are the coordinates + in `bug-hunter--current-file' where the expression starts. +It is expected that one of EXPR is either throwing an error or +causing some undesirable effect (which triggers ASSERTION). + +ASSERTION is either nil or an expression. + If nil, EXPRs are bisected until we find the first one that + throws errors. + If it is an expression, EXPRs are bisected by testing + ASSERTION. It should return nil if all is fine (e.g. if used + with \"emacs -Q\"), and should return non-nil when a problem + is detected. + +Bug hunter will refuse to hunt if (i) an error is signaled or the +assertion is triggered while running emacs -Q, or (ii) no errors +are signaled and the assertion is not triggered after all EXPRs +are evaluated." + (let ((expressions (unless (eq (car-safe rich-forms) 'bug-caught) + (mapcar #'car rich-forms))) + (bug-hunter--estimate (ceiling (log (length rich-forms) 2)))) + ;; Prepare buffer, and make sure they've seen it. + (switch-to-buffer (bug-hunter--init-report-buffer assertion bug-hunter--estimate)) + (when (eq assertion 'interactive) + (read-char-choice "Please read the instructions above and type 6 when ready. " '(?6))) + + (cond + ;; Check for errors when reading the init file. + ((not expressions) + (apply #'bug-hunter--report-error (cdr rich-forms)) + (apply #'vector (cdr rich-forms))) + + ;; Make sure there's a bug to hunt. + ((progn (bug-hunter--report "Doing some initial tests...") + (not (bug-hunter--run-and-test expressions assertion))) + (bug-hunter--report-user-error "Test failed.\n%s\n%s" + (if assertion + (concat "The assertion returned nil after loading the entire file.\n" + bug-hunter--assertion-reminder) + "No errors signaled after loading the entire file. +If you're looking for something that's not an error, use the +interactive hunt instead of the error hunt. If you have some +elisp proficiency, you can also use the assertion hunt, see this +link for some examples: + https://github.com/Malabarba/elisp-bug-hunter") + (or assertion ""))) + + ;; Make sure we're in a forest, not a volcano. + ((bug-hunter--run-and-test nil assertion) + (bug-hunter--report-user-error "Test failed.\n%s\n%s" + (if assertion + (concat "Assertion returned non-nil even on emacs -Q:" + bug-hunter--assertion-reminder) + "Detected a signaled error even on emacs -Q. This could mean three +things things: +1. The problem happens inside `package-initialize'. +2. You wrote the assertion wrong. +3. There's something seriously wrong with your Emacs installation.") + (or assertion ""))) + + (t + ;; Perform the actual hunt. + (bug-hunter--report "Initial tests done. Hunting for the cause...") + (let* ((result (bug-hunter--bisect-start expressions assertion))) + (if (not result) + (bug-hunter--report-user-error "No problem was found, despite our initial tests.\n%s" + "I have no idea what's going on.") + (let* ((pos (elt result 0)) + (ret (elt result 1)) + (linecol (when pos (cdr (elt rich-forms pos)))) + (expression (when pos (elt expressions pos)))) + (if (eq (car-safe ret) 'bug-caught) + (bug-hunter--report-error + (cdr ret) (car linecol) (cadr linecol) expression) + (bug-hunter--report-error + (list 'assertion-triggered ret) + (car linecol) (cadr linecol) expression))))))))) + +(defconst bug-hunter--hunt-type-prompt + "To bisect interactively, type i +To use automatic error detection, type e +To provide a lisp assertion, type a +=> ") + +(defun bug-hunter--read-from-minibuffer () + "Read a list of expressions from the minibuffer. +Wraps them in a progn if necessary to always return a single +form. + +The user may decide to not provide input, in which case +'interactive is returned. Note, this is different from the user +typing `RET' at an empty prompt, in which case nil is returned." + (pcase (read-char-choice (if (display-graphic-p) + bug-hunter--hunt-type-prompt + (replace-regexp-in-string "To bisect interactively,.*\n" "" + bug-hunter--hunt-type-prompt)) + '(?i ?e ?a)) + (`?i + (unless (display-graphic-p) + (user-error "Sorry, but `interactive' bisection needs a graphical frame")) + 'interactive) + (`?e nil) + (_ + (require 'simple) + (let ((exprs + (with-temp-buffer + ;; Copied from `read--expression'. + (let ((minibuffer-completing-symbol t)) + (minibuffer-with-setup-hook + (lambda () + (add-hook 'completion-at-point-functions + (if (fboundp 'elisp-completion-at-point) + #'elisp-completion-at-point + (with-no-warnings + #'lisp-completion-at-point)) + nil t) + (run-hooks 'eval-expression-minibuffer-setup-hook)) + (insert + (read-from-minibuffer + "Provide an assertion. This is a lisp expression that returns nil if (and only if) everything is fine:\n => " + nil read-expression-map nil 'read-expression-history)))) + (goto-char (point-min)) + (mapcar #'car (bug-hunter--read-buffer))))) + (if (cdr exprs) + (cons #'progn exprs) + (car exprs)))))) + +;;;###autoload +(defun bug-hunter-file (file &optional assertion) + "Bisect FILE while testing ASSERTION. +All sexps in FILE are read and passed to `bug-hunter-hunt' as a +list. See `bug-hunter-hunt' for how to use ASSERTION." + (interactive + (list + (read-file-name "File to bisect: " + (file-name-directory (or (buffer-file-name) "./")) + nil t + (file-name-nondirectory (or (buffer-file-name) "./"))) + (bug-hunter--read-from-minibuffer))) + (let ((bug-hunter--current-file file)) + (bug-hunter-hunt (bug-hunter--read-contents file) assertion))) + +;;;###autoload +(defun bug-hunter-init-file (&optional assertion) + "Test ASSERTION throughout `user-init-file'. +All sexps inside `user-init-file' are read and passed to +`bug-hunter-hunt' as a list. See `bug-hunter-hunt' for how to use +ASSERTION." + (interactive (list (bug-hunter--read-from-minibuffer))) + (bug-hunter-file user-init-file assertion)) + +;;;; ChangeLog: + +;; 2016-04-30 Artur Malabarba +;; +;; Fix #17 - Minibuffer completion pre 25 +;; +;; 2016-04-19 Artur Malabarba +;; +;; bug-hunter: Improve the error message when emacs -Q fails +;; +;; 2016-04-01 Artur Malabarba +;; +;; bug-hunter: Don't offer interactive bisection in the terminal +;; +;; 2016-02-09 Artur Malabarba +;; +;; * packages/bug-hunter: Better error reports and instructions +;; +;; 2015-12-01 Artur Malabarba +;; +;; packages/bug-hunter: Username and documentation +;; +;; 2015-08-16 Artur Malabarba +;; +;; bug-hunter: Better messages +;; +;; 2015-08-16 Artur Malabarba +;; +;; bug-hunter: Version 1.0 +;; +;; 2015-08-16 Artur Malabarba +;; +;; bug-hunter: Indentation and documentation +;; +;; 2015-06-15 Artur Malabarba +;; +;; packages/bug-hunter: Interactive hunt +;; +;; 2015-04-26 Artur Malabarba +;; +;; * bug-hunter.el: Bump version number +;; +;; Also expand documentation. +;; +;; 2015-04-26 Artur Malabarba +;; +;; * bug-hunter.el: Reduce number of steps +;; +;; 2015-04-26 Artur Malabarba +;; +;; * bug-hunter.el: Fix a couple of bugs +;; +;; 2015-03-25 Artur Malabarba +;; +;; packages/bug-hunter: New package +;; + + +(provide 'bug-hunter) +;;; bug-hunter.el ends here diff --git a/elpa/bug-hunter-readme.txt b/elpa/bug-hunter-readme.txt index e69de29b..d8c9463a 100644 --- a/elpa/bug-hunter-readme.txt +++ b/elpa/bug-hunter-readme.txt @@ -0,0 +1,92 @@ +#+OPTIONS: toc:nil num:nil +#+TITLE: [[file:hunter.png]] The Bug Hunter [[https://travis-ci.org/Malabarba/elisp-bug-hunter][file:https://travis-ci.org/Malabarba/elisp-bug-hunter.svg?branch=master]] + +/Automatically debug and bisect your init (.emacs) file!/ + +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~). + +[[file:hunter-screencast.gif]] + +* Usage Examples + +** Automated error hunting +If your Emacs init file signals an error during startup, but you don’t +know why, simply issue +#+BEGIN_SRC text +M-x bug-hunter-init-file RET e +#+END_SRC +and The Bug Hunter will find it for you. Note that your ~init.el~ +(or ~.emacs~) must be idempotent for this to work. + +** Interactive hunt + +If Emacs starts up without errors but something is not working as it +should, invoke the same command, but choose the interactive option: +#+BEGIN_SRC text +M-x bug-hunter-init-file RET i +#+END_SRC +The Bug Hunter will start a separate Emacs instance several times, and +then it will ask you each time whether that instance presented the +problem you have. After doing this about 5--12 times, you’ll be given +the results. + +** Assertion hunt + +The Bug Hunter can also find your issue based on an assertion. +Essentially, if you can write a code snippet that returns non-nil when +it detects the issue, just provide this snippet as the assertion and +the Bug Hunter will do the rest. + +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? + +#+BEGIN_SRC text +M-x bug-hunter-init-file RET a (featurep 'cl) RET +#+END_SRC + +*That’s it!* You’ll be given a nice buffer reporting the results: + +[[file:cl-example.png]] +- 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. +- 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! + +Finally, you can also use ~bug-hunter-file~ to hunt in other files. + +* Installation +The Bug Hunter is available from [[https://elpa.gnu.org/packages/bug-hunter.html][GNU Elpa]] to all Emacs versions since +~24.1~. To install, just issue + +#+BEGIN_SRC text +M-x package-install RET bug-hunter +#+END_SRC + +* init.org and other literate-style configs + +Some people (me included) like to organize their init files by +writting it in ~org-mode~ instead of Emacs-Lisp. This usually involves +adding something like this to ~init.el~, +#+BEGIN_SRC emacs-lisp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Maybe some code up here ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(require 'org) +(org-babel-tangle-file "~/.emacs.d/org-init.org" + "~/.emacs.d/org-init.el") +(load "~/.emacs.d/org-init.el") +#+END_SRC + +At first, this makes the Bug-Hunter essentially useless, for it will +do the hunting in ~init.el~ instead of the much more extensive +~org-init.el~. The name of the second file (~org-init.el~) will vary, +but the point is the same. But fear not! There’s a simple solution: + +1. If you have any code above the call to ~org-babel-tangle-file~, copy that to the top of ~org-init.el~ (or whatever is the name of your tangled file). This includes that ~(require 'org)~ over there. +2. Invoke ~M-x~ ~bug-hunter-file~ (instead of ~bug-hunter-init-file~). It will ask you which file to debug, and you need to point it to your tangled output file ~org-init.el~. -- 2.47.3