unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "Jarosław Rzeszótko" <jrzeszotko@gmail.com>
To: "emacs-devel@gnu.org" <emacs-devel@gnu.org>
Subject: Making debugging possible on expressions executed from ielm
Date: Sat, 13 Jan 2018 10:22:25 +0100	[thread overview]
Message-ID: <CAO_X8WBhjU0J+v5owHu9BUsOdc+zNvrt_-yq2DWQWb=dHKzd=Q@mail.gmail.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 1682 bytes --]

I attach a patch for ielm.el that makes it possible to enter the debugger
when executing expressions in ielm, and that makes ielm respect the
debug-on-error and debug-on-exit flags.

I guess that possibly the reason that was not originally done is that it is
not obvious how to restore ielm to a usable state after the debugger has
been entered. My patch makes use of the fact that unwind-protect unwind
forms are still executed when user resumes execution from the debugger
after an error.

Without the patch, ielm wraps the evaluation of the expression given by the
user in a condition case, and in case of an error, or exit, displays an
appropriate message in its buffer, right under the evaluated expression,
regardless of debug-on-error and debug-on-exit.

With the patch, the message that ielm displays in its buffer will be a
generic error message  regardless if there was an error in the evaluated
form, or a quit. However:

- if debug-on-error is t, emacs will enter the debugger and show a stack
trace, just like with almost any other evaluation method. When the user
continues the execution from the debugger, ielm will correctly resume
execution

- if debug-on-error is nil, emacs will display the specific error in the
minibuffer anyway

Similar things are true for debug-on-quit.

I think this is an improvement over the current state of affairs. Of course
ideally I would like to preserve the "nice" message in ielm buffer and make
the improvements I made, but emacs does not seem to make it possible to do
some handling of an error and then to re-raise it while preserving the
original stack trace.

Cheers,
Jarosław Rzeszótko

[-- Attachment #1.2: Type: text/html, Size: 1936 bytes --]

[-- Attachment #2: ielm-debug-on-error.el.patch --]
[-- Type: text/x-patch, Size: 12410 bytes --]

diff --git a/lisp/ielm.el b/lisp/ielm.el
index fb285e80f6..13bb4693cd 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -353,113 +353,117 @@ nonempty, then flushes the buffer."
         (output "")                  ; result to display
         (wbuf ielm-working-buffer)   ; current buffer after evaluation
         (pmark (ielm-pm)))
-    (unless (ielm-is-whitespace-or-comment string)
-      (condition-case err
-          (let ((rout (read-from-string string)))
-            (setq form (car rout)
-                  pos (cdr rout)))
-        (error (setq result (error-message-string err))
-               (setq error-type "Read error")))
-      (unless error-type
-        ;; Make sure working buffer has not been killed
-        (if (not (buffer-name ielm-working-buffer))
-            (setq result "Working buffer has been killed"
-                  error-type "IELM Error"
-                  wbuf (current-buffer))
-          (if (ielm-is-whitespace-or-comment (substring string pos))
-              ;; To correctly handle the ielm-local variables *,
-              ;; ** and ***, we need a temporary buffer to be
-              ;; current at entry to the inner of the next two let
-              ;; forms.  We need another temporary buffer to exit
-              ;; that same let.  To avoid problems, neither of
-              ;; these buffers should be alive during the
-              ;; evaluation of form.
-              (let* ((*1 *)
-                     (*2 **)
-                     (*3 ***)
-                     (active-process (ielm-process))
-                     (old-standard-output standard-output)
-                     new-standard-output
-                     ielm-temp-buffer)
-                (set-match-data ielm-match-data)
-                (save-excursion
-                  (with-temp-buffer
-                    (condition-case err
-                        (unwind-protect
-                            ;; The next let form creates default
-                            ;; bindings for *, ** and ***.  But
-                            ;; these default bindings are
-                            ;; identical to the ielm-local
-                            ;; bindings.  Hence, during the
-                            ;; evaluation of form, the
-                            ;; ielm-local values are going to be
-                            ;; used in all buffers except for
-                            ;; other ielm buffers, which override
-                            ;; them.  Normally, the variables *1,
-                            ;; *2 and *3 also have default
-                            ;; bindings, which are not overridden.
-                            (let ((* *1)
-                                  (** *2)
-                                  (*** *3))
-                              (when (eq standard-output t)
-                                (setf new-standard-output
-                                      (ielm-standard-output-impl
-                                       active-process))
-                                (setf standard-output new-standard-output))
-                              (kill-buffer (current-buffer))
-                              (set-buffer wbuf)
-                              (setq result
-                                    (eval form lexical-binding))
-                              (setq wbuf (current-buffer))
-                              (setq
-                               ielm-temp-buffer
-                               (generate-new-buffer " *ielm-temp*"))
-                              (set-buffer ielm-temp-buffer))
-                          (when ielm-temp-buffer
-                            (kill-buffer ielm-temp-buffer))
-                          (when (eq new-standard-output standard-output)
-                            (ignore-errors
-                              (funcall standard-output t))
-                            (setf standard-output old-standard-output)))
-                      (error (setq result (error-message-string err))
-                             (setq error-type "Eval error"))
-                      (quit (setq result "Quit during evaluation")
-                            (setq error-type "Eval error")))))
-                (setq ielm-match-data (match-data)))
-            (setq error-type "IELM error")
-            (setq result "More than one sexp in input"))))
-
-      ;; If the eval changed the current buffer, mention it here
-      (unless (eq wbuf ielm-working-buffer)
-        (message "current buffer is now: %s" wbuf)
-        (setq ielm-working-buffer wbuf))
-
-      (goto-char pmark)
-      (unless error-type
-        (condition-case nil
-            ;; Self-referential objects cause loops in the printer, so
-            ;; trap quits here. May as well do errors, too
-            (unless for-effect
-              (setq output (concat output (pp-to-string result)
-				   (let ((str (eval-expression-print-format result)))
-				     (if str (propertize str 'font-lock-face 'shadow))))))
-          (error (setq error-type "IELM Error")
-                 (setq result "Error during pretty-printing (bug in pp)"))
-          (quit  (setq error-type "IELM Error")
-                 (setq result "Quit during pretty-printing"))))
-      (if error-type
+    (unwind-protect
+        (progn
+          (unless (ielm-is-whitespace-or-comment string)
+            (condition-case err
+                (let ((rout (read-from-string string)))
+                  (setq form (car rout)
+                        pos (cdr rout)))
+              (error (setq result (error-message-string err))
+                     (setq error-type "Read error")))
+            (unless error-type
+              ;; Make sure working buffer has not been killed
+              (if (not (buffer-name ielm-working-buffer))
+                  (setq result "Working buffer has been killed"
+                        error-type "IELM Error"
+                        wbuf (current-buffer))
+                (if (ielm-is-whitespace-or-comment (substring string pos))
+                    ;; To correctly handle the ielm-local variables *,
+                    ;; ** and ***, we need a temporary buffer to be
+                    ;; current at entry to the inner of the next two let
+                    ;; forms.  We need another temporary buffer to exit
+                    ;; that same let.  To avoid problems, neither of
+                    ;; these buffers should be alive during the
+                    ;; evaluation of form.
+                    (let* ((*1 *)
+                           (*2 **)
+                           (*3 ***)
+                           (active-process (ielm-process))
+                           (old-standard-output standard-output)
+                           new-standard-output
+                           ielm-temp-buffer)
+                      (set-match-data ielm-match-data)
+                      (save-excursion
+                        (with-temp-buffer
+                          (unwind-protect
+                              ;; The next let form creates default
+                              ;; bindings for *, ** and ***.  But
+                              ;; these default bindings are
+                              ;; identical to the ielm-local
+                              ;; bindings.  Hence, during the
+                              ;; evaluation of form, the
+                              ;; ielm-local values are going to be
+                              ;; used in all buffers except for
+                              ;; other ielm buffers, which override
+                              ;; them.  Normally, the variables *1,
+                              ;; *2 and *3 also have default
+                              ;; bindings, which are not overridden.
+                              (let ((* *1)
+                                    (** *2)
+                                    (*** *3))
+                                (setq result nil)
+                                (when (eq standard-output t)
+                                  (setf new-standard-output
+                                        (ielm-standard-output-impl
+                                         active-process))
+                                  (setf standard-output new-standard-output))
+                                (kill-buffer (current-buffer))
+                                (set-buffer wbuf)
+                                ;; Will immediately get
+                                ;; overwritten, but only if eval
+                                ;; does not throw an error
+                                (setq error-type "Eval error")
+                                (setq result
+                                      (eval form lexical-binding))
+                                (setq error-type nil)
+                                (setq wbuf (current-buffer))
+                                (setq
+                                 ielm-temp-buffer
+                                 (generate-new-buffer " *ielm-temp*"))
+                                (set-buffer ielm-temp-buffer))
+                            (when ielm-temp-buffer
+                              (kill-buffer ielm-temp-buffer))
+                            (when (eq new-standard-output standard-output)
+                              (ignore-errors
+                                (funcall standard-output t))
+                              (setf standard-output old-standard-output)))))
+                      (setq ielm-match-data (match-data)))
+                  (setq error-type "IELM error")
+                  (setq result "More than one sexp in input"))))
+
+            ;; If the eval changed the current buffer, mention it here
+            (unless (eq wbuf ielm-working-buffer)
+              (message "current buffer is now: %s" wbuf)
+              (setq ielm-working-buffer wbuf))
+
+            (goto-char pmark)
+            (unless error-type
+              (condition-case nil
+                  ;; Self-referential objects cause loops in the printer, so
+                  ;; trap quits here. May as well do errors, too
+                  (unless for-effect
+                    (setq output (concat output (pp-to-string result)
+                                         (let ((str (eval-expression-print-format result)))
+                                           (if str (propertize str 'font-lock-face 'shadow))))))
+                (error (setq error-type "IELM Error")
+                       (setq result "Error during pretty-printing (bug in pp)"))
+                (quit  (setq error-type "IELM Error")
+                       (setq result "Quit during pretty-printing"))))))
           (progn
-            (when ielm-noisy (ding))
-            (setq output (concat output "*** " error-type " ***  "))
-            (setq output (concat output result)))
-        ;; There was no error, so shift the *** values
-        (setq *** **)
-        (setq ** *)
-        (setq * result))
-      (when (or (not for-effect) (not (equal output "")))
-        (setq output (concat output "\n"))))
-    (setq output (concat output ielm-prompt-internal))
-    (comint-output-filter (ielm-process) output)))
+            (if error-type
+                (progn
+                  (when ielm-noisy (ding))
+                  (setq output (concat output "*** " error-type " ***  "))
+                  (setq output (concat output result)))
+              ;; There was no error, so shift the *** values
+              (setq *** **)
+              (setq ** *)
+              (setq * result))
+            (when (or (not for-effect) (not (equal output "")))
+              (setq output (concat output "\n"))))
+          (setq output (concat output ielm-prompt-internal))
+          (comint-output-filter (ielm-process) output))))
 
 ;;; Process and marker utilities
 
@@ -517,9 +521,6 @@ causes output to be directed to the ielm buffer.
 set to a different value during evaluation.  You can use (princ
 VALUE) or (pp VALUE) to write to the ielm buffer.
 
-Expressions evaluated by IELM are not subject to `debug-on-quit' or
-`debug-on-error'.
-
 The behavior of IELM may be customized with the following variables:
 * To stop beeping on error, set `ielm-noisy' to nil.
 * If you don't like the prompt, you can change it by setting `ielm-prompt'.

             reply	other threads:[~2018-01-13  9:22 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-13  9:22 Jarosław Rzeszótko [this message]
2018-01-15 17:23 ` Making debugging possible on expressions executed from ielm Stefan Monnier
2018-01-15 19:22 ` Jarosław Rzeszótko
2018-01-15 21:59   ` Stefan Monnier
2018-01-16  6:42     ` Jarosław Rzeszótko
2018-01-19 19:14   ` Jarosław Rzeszótko
2018-01-19 19:58     ` Eli Zaretskii
2018-01-20 17:55   ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAO_X8WBhjU0J+v5owHu9BUsOdc+zNvrt_-yq2DWQWb=dHKzd=Q@mail.gmail.com' \
    --to=jrzeszotko@gmail.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).