From 62019d9ff4b8af07bbfb8294751811dcad79ed99 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 29 Aug 2023 17:02:40 -0700 Subject: [PATCH 1/2] Fix handling of Eshell debug modes Previously, these were enabled/disabled at byte-compilation time, but we want to control them at runtime. * lisp/eshell/esh-cmd.el (eshell-eval-command): Call 'eshell-debug-command-start'. (eshell-manipulate): Check 'eshell-debug-command' at runtime. Update callers. (eshell-debug-command): Move to "esh-util.el". (eshell/eshell-debug, pcomplate/eshell-mode/eshell-debug): Move to "em-basic.el". (eshell-debug-show-parsed-args): Update implementation. * lisp/eshell/esh-util.el (eshell-debug-command): Move from "esh-cmd.el" and convert to a list. (eshell-debug-command-buffer): New variable. (eshell-condition-case): Check 'eshell-handle-errors' at runtime. (eshell-debug-command-start): New function. (eshell-debug-command): Move from "esh-cmd.el" and convert to a macro. * lisp/eshell/em-basic.el (eshell/eshell-debug) (pcomplete/eshell-mode/eshell-debug): Move from "esh-cmd.el" and reimplement. * lisp/eshell/eshell.el (eshell-command): Pass the original input to 'eshell-eval-command'. * doc/misc/eshell.texi (Built-ins): Update documentation for 'eshell-debug'. --- doc/misc/eshell.texi | 21 ++++++-- lisp/eshell/em-basic.el | 32 ++++++++++++ lisp/eshell/esh-cmd.el | 105 +++++++++------------------------------- lisp/eshell/esh-util.el | 44 +++++++++++++++-- lisp/eshell/eshell.el | 3 +- 5 files changed, 113 insertions(+), 92 deletions(-) diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index f8f60bae13a..09aafdcf070 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -619,10 +619,23 @@ Built-ins @item eshell-debug @cmindex eshell-debug Toggle debugging information for Eshell itself. You can pass this -command the argument @code{errors} to enable/disable Eshell trapping -errors when evaluating commands, or the argument @code{commands} to -show/hide command execution progress in the buffer @code{*eshell last -cmd*}. +command one or more of the following arguments: + +@itemize @bullet + +@item +@code{error}, to enable/disable Eshell trapping errors when evaluating +commands; + +@item +@code{form}, to show/hide Eshell command form manipulation in the +buffer @code{*eshell last cmd*}; or + +@item +@code{process}, to show/hide external process events in the buffer +@code{*eshell last cmd*}. + +@end itemize @item exit @cmindex exit diff --git a/lisp/eshell/em-basic.el b/lisp/eshell/em-basic.el index 016afe811b2..cea4b78129f 100644 --- a/lisp/eshell/em-basic.el +++ b/lisp/eshell/em-basic.el @@ -188,6 +188,38 @@ eshell/umask (put 'eshell/umask 'eshell-no-numeric-conversions t) +(defun eshell/eshell-debug (&rest args) + "A command for toggling certain debug variables." + (eshell-eval-using-options + "eshell-debug" args + '((?h "help" nil nil "display this usage message") + :usage "[kinds]... +This command is used to aid in debugging problems related to Eshell +itself. It is not useful for anything else. The recognized `kinds' +are: + + error stops Eshell from trapping errors + form shows command form manipulation in `*eshell last cmd*' + process shows process events in `*eshell last cmd*'") + (if args + (dolist (kind args) + (if (equal kind "error") + (setq eshell-handle-errors (not eshell-handle-errors)) + (let ((kind-sym (intern kind))) + (if (memq kind-sym eshell-debug-command) + (setq eshell-debug-command + (delq kind-sym eshell-debug-command)) + (push kind-sym eshell-debug-command))))) + ;; Output the currently-enabled debug kinds. + (unless eshell-handle-errors + (eshell-print "error\n")) + (dolist (kind eshell-debug-command) + (eshell-printn (symbol-name kind)))))) + +(defun pcomplete/eshell-mode/eshell-debug () + "Completion for the `debug' command." + (while (pcomplete-here '("error" "form" "process")))) + (provide 'em-basic) ;; Local Variables: diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 80066263396..ed2d6c71fc8 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -237,17 +237,6 @@ eshell-cmd-load-hook :version "24.1" ; removed eshell-cmd-initialize :type 'hook) -(defcustom eshell-debug-command nil - "If non-nil, enable Eshell debugging code. -This is slow, and only useful for debugging problems with Eshell. -If you change this without using customize after Eshell has loaded, -you must re-load `esh-cmd.el'." - :initialize 'custom-initialize-default - :set (lambda (symbol value) - (set symbol value) - (load "esh-cmd")) - :type 'boolean) - (defcustom eshell-deferrable-commands '(eshell-named-command eshell-lisp-command @@ -436,22 +425,9 @@ eshell-parse-command (run-hooks 'eshell-post-command-hook))) (macroexp-progn commands)))) -(defun eshell-debug-command (tag subform) - "Output a debugging message to `*eshell last cmd*'." - (let ((buf (get-buffer-create "*eshell last cmd*")) - (text (eshell-stringify eshell-current-command))) - (with-current-buffer buf - (if (not tag) - (erase-buffer) - (insert "\n\C-l\n" tag "\n\n" text - (if subform - (concat "\n\n" (eshell-stringify subform)) "")))))) - (defun eshell-debug-show-parsed-args (terms) "Display parsed arguments in the debug buffer." - (ignore - (if eshell-debug-command - (eshell-debug-command "parsed arguments" terms)))) + (ignore (eshell-debug-command 'form "parsed arguments" terms))) (defun eshell-no-command-conversion (terms) "Don't convert the command argument." @@ -942,38 +918,6 @@ eshell-command-to-value ;; finishes, it will resume the evaluation using the remainder of the ;; command tree. -(defun eshell/eshell-debug (&rest args) - "A command for toggling certain debug variables." - (ignore - (cond - ((not args) - (if eshell-handle-errors - (eshell-print "errors\n")) - (if eshell-debug-command - (eshell-print "commands\n"))) - ((member (car args) '("-h" "--help")) - (eshell-print "usage: eshell-debug [kinds] - -This command is used to aid in debugging problems related to Eshell -itself. It is not useful for anything else. The recognized `kinds' -at the moment are: - - errors stops Eshell from trapping errors - commands shows command execution progress in `*eshell last cmd*' -")) - (t - (while args - (cond - ((string= (car args) "errors") - (setq eshell-handle-errors (not eshell-handle-errors))) - ((string= (car args) "commands") - (setq eshell-debug-command (not eshell-debug-command)))) - (setq args (cdr args))))))) - -(defun pcomplete/eshell-mode/eshell-debug () - "Completion for the `debug' command." - (while (pcomplete-here '("errors" "commands")))) - (iter-defun eshell--find-subcommands (haystack) "Recursively search for subcommand forms in HAYSTACK. This yields the SUBCOMMANDs when found in forms like @@ -1049,10 +993,7 @@ eshell-eval-command (if here (eshell-update-markers here)) (eshell-do-eval ',command)))) - (and eshell-debug-command - (with-current-buffer (get-buffer-create "*eshell last cmd*") - (erase-buffer) - (insert "command: \"" input "\"\n"))) + (eshell-debug-command-start input) (setq eshell-current-command command) (let* (result (delim (catch 'eshell-incomplete @@ -1088,17 +1029,17 @@ eshell-resume-eval (error (error (error-message-string err))))) -(defmacro eshell-manipulate (tag &rest commands) - "Manipulate a COMMAND form, with TAG as a debug identifier." - (declare (indent 1)) - ;; Check `bound'ness since at compile time the code until here has not - ;; executed yet. - (if (not (and (boundp 'eshell-debug-command) eshell-debug-command)) - `(progn ,@commands) - `(progn - (eshell-debug-command ,(eval tag) form) - ,@commands - (eshell-debug-command ,(concat "done " (eval tag)) form)))) +(defmacro eshell-manipulate (form tag &rest body) + "Manipulate a command FORM with BODY, using TAG as a debug identifier." + (declare (indent 2)) + (let ((tag-symbol (make-symbol "tag"))) + `(if (not (memq 'form eshell-debug-command)) + (progn ,@body) + (let ((,tag-symbol ,tag)) + (eshell-debug-command 'form ,tag-symbol ,form 'always) + ,@body + (eshell-debug-command 'form (concat "done " ,tag-symbol) ,form + 'always))))) (defun eshell-do-eval (form &optional synchronous-p) "Evaluate FORM, simplifying it as we go. @@ -1125,8 +1066,8 @@ eshell-do-eval ;; we can modify any `let' forms to evaluate only once. (if (macrop (car form)) (let ((exp (copy-tree (macroexpand form)))) - (eshell-manipulate (format-message "expanding macro `%s'" - (symbol-name (car form))) + (eshell-manipulate form + (format-message "expanding macro `%s'" (symbol-name (car form))) (setcar form (car exp)) (setcdr form (cdr exp))))) (let ((args (cdr form))) @@ -1138,7 +1079,7 @@ eshell-do-eval (let ((new-form (copy-tree `(let ((eshell--command-body nil) (eshell--test-body nil)) (eshell--wrapped-while ,@args))))) - (eshell-manipulate "modifying while form" + (eshell-manipulate form "modifying while form" (setcar form (car new-form)) (setcdr form (cdr new-form))) (eshell-do-eval form synchronous-p))) @@ -1161,7 +1102,7 @@ eshell-do-eval (setq eshell--command-body nil eshell--test-body (copy-tree (car args))))) ((eq (car form) 'if) - (eshell-manipulate "evaluating if condition" + (eshell-manipulate form "evaluating if condition" (setcar args (eshell-do-eval (car args) synchronous-p))) (eshell-do-eval (cond @@ -1180,7 +1121,7 @@ eshell-do-eval (eval form)) ((eq (car form) 'let) (unless (eq (car-safe (cadr args)) 'eshell-do-eval) - (eshell-manipulate "evaluating let args" + (eshell-manipulate form "evaluating let args" (dolist (letarg (car args)) (when (and (listp letarg) (not (eq (cadr letarg) 'quote))) @@ -1207,7 +1148,7 @@ eshell-do-eval ;; the let-bindings' values so that those values are ;; correct when we resume evaluation of this form. (when deferred - (eshell-manipulate "rebinding let args after `eshell-defer'" + (eshell-manipulate form "rebinding let args after `eshell-defer'" (let ((bindings (car args))) (while bindings (let ((binding (if (consp (car bindings)) @@ -1232,7 +1173,7 @@ eshell-do-eval (unless (eq (car form) 'unwind-protect) (setq args (cdr args))) (unless (eq (caar args) 'eshell-do-eval) - (eshell-manipulate "handling special form" + (eshell-manipulate form "handling special form" (setcar args `(eshell-do-eval ',(car args) ,synchronous-p)))) (eval form)) ((eq (car form) 'setq) @@ -1242,7 +1183,7 @@ eshell-do-eval (list 'quote (eval form))) (t (if (and args (not (memq (car form) '(run-hooks)))) - (eshell-manipulate + (eshell-manipulate form (format-message "evaluating arguments to `%s'" (symbol-name (car form))) (while args @@ -1283,7 +1224,7 @@ eshell-do-eval (setq result (eval form)))))) (if new-form (progn - (eshell-manipulate "substituting replacement form" + (eshell-manipulate form "substituting replacement form" (setcar form (car new-form)) (setcdr form (cdr new-form))) (eshell-do-eval form synchronous-p)) @@ -1292,7 +1233,7 @@ eshell-do-eval (procs (eshell-make-process-pair result))) (if synchronous-p (eshell/wait (cdr procs)) - (eshell-manipulate "inserting ignore form" + (eshell-manipulate form "inserting ignore form" (setcar form 'ignore) (setcdr form nil)) (throw 'eshell-defer procs)) diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el index 87cd1f5dcb2..3a318056445 100644 --- a/lisp/eshell/esh-util.el +++ b/lisp/eshell/esh-util.el @@ -102,6 +102,15 @@ eshell-ange-ls-uids (string :tag "Username") (repeat :tag "UIDs" string)))))) +(defcustom eshell-debug-command nil + "A list of debug features to enable when running Eshell commands. +Possible entries are `form', to log the manipulation of Eshell +command forms, and `io', to log I/O operations. + +If nil, don't debug commands at all." + :version "30.1" + :type '(set (const :tag "Form manipulation" form))) + ;;; Internal Variables: (defvar eshell-number-regexp @@ -145,6 +154,9 @@ eshell-command-output-properties ,#'eshell--mark-yanked-as-output)) "A list of text properties to apply to command output.") +(defvar eshell-debug-command-buffer "*eshell last cmd*" + "The name of the buffer to log debug messages about command invocation.") + ;;; Obsolete variables: (define-obsolete-variable-alias 'eshell-host-names @@ -164,11 +176,33 @@ eshell-condition-case "If `eshell-handle-errors' is non-nil, this is `condition-case'. Otherwise, evaluates FORM with no error handling." (declare (indent 2) (debug (sexp form &rest form))) - (if eshell-handle-errors - `(condition-case-unless-debug ,tag - ,form - ,@handlers) - form)) + `(if eshell-handle-errors + (condition-case-unless-debug ,tag + ,form + ,@handlers) + ,form)) + +(defun eshell-debug-command-start (command) + "Start debugging output for the command string COMMAND. +If debugging is enabled (see `eshell-debug-command'), this will +start logging to `*eshell last cmd*'." + (when eshell-debug-command + (with-current-buffer (get-buffer-create eshell-debug-command-buffer) + (erase-buffer) + (insert "command: \"" command "\"\n")))) + +(defmacro eshell-debug-command (kind message &optional form always) + "Output a debugging message to `*eshell last cmd*' if debugging is enabled. +KIND is the kind of message to log (either `form' or `io'). If +present in `eshell-debug-command' (or if ALWAYS is non-nil), +output this message; otherwise, ignore it." + (let ((kind-sym (make-symbol "kind"))) + `(let ((,kind-sym ,kind)) + (when ,(or always `(memq ,kind-sym eshell-debug-command)) + (with-current-buffer (get-buffer-create eshell-debug-command-buffer) + (insert "\n\C-l\n[" (symbol-name ,kind-sym) "] " ,message) + (when-let ((form ,form)) + (insert "\n\n" (eshell-stringify form)))))))) (defun eshell--mark-as-output (start end &optional object) "Mark the text from START to END as Eshell output. diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el index 15fc2ae6310..cbd0de3c093 100644 --- a/lisp/eshell/eshell.el +++ b/lisp/eshell/eshell.el @@ -301,7 +301,8 @@ eshell-command `(let ((eshell-current-handles (eshell-create-handles ,stdout 'insert)) (eshell-current-subjob-p)) - ,(eshell-parse-command command)))) + ,(eshell-parse-command command)) + command)) intr (bufname (if (eq (car-safe proc) :eshell-background) "*Eshell Async Command Output*" -- 2.25.1