1

I got the below function from an article on howardism.org. Its purpose is to exit eshell and close the corresponding window.

;; GNU Emacs 26.3 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.10) of 2019-08-29
(defun eshell/x ()
  (insert "exit")
  (eshell-send-input)
  (delete-window))

(setq debug-on-error t)

Looked pretty straightforward to me. But when I start emacs -Q (See above code block for version info), then evaluate the above code and type x<RET> in an eshell window. I get the following:

Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
  eshell-lisp-command(eshell/x nil)
  eshell-plain-command("x" nil)
  eshell-named-command("x")
  (prog1 (eshell-named-command "x") (run-hooks (quote eshell-this-command-hook)))
  (condition-case err (prog1 (eshell-named-command "x") (run-hooks (quote eshell-this-command-hook))) ((debug error) (run-hooks (quote eshell-this-command-hook)) (eshell-errorn (error-message-string err)) (eshell-close-handles 1)))
  (condition-case-unless-debug err (prog1 (eshell-named-command "x") (run-hooks (quote eshell-this-command-hook))) (error (run-hooks (quote eshell-this-command-hook)) (eshell-errorn (error-message-string err)) (eshell-close-handles 1)))
  (eshell-condition-case err (prog1 (eshell-named-command "x") (run-hooks (quote eshell-this-command-hook))) (error (run-hooks (quote eshell-this-command-hook)) (eshell-errorn (error-message-string err)) (eshell-close-handles 1)))
  (let ((eshell-this-command-hook (quote (ignore)))) (eshell-condition-case err (prog1 (eshell-named-command "x") (run-hooks (quote eshell-this-command-hook))) (error (run-hooks (quote eshell-this-command-hook)) (eshell-errorn (error-message-string err)) (eshell-close-handles 1))))
  (eshell-trap-errors (eshell-named-command "x"))
  (progn (eshell-trap-errors (eshell-named-command "x")))
  (catch (quote top-level) (progn (eshell-trap-errors (eshell-named-command "x"))))
  (progn (run-hooks (quote eshell-pre-command-hook)) (catch (quote top-level) (progn (eshell-trap-errors (eshell-named-command "x")))) (run-hooks (quote eshell-post-command-hook)))
  (let ((eshell-current-handles (eshell-create-handles t (quote append))) eshell-current-subjob-p) (progn (run-hooks (quote eshell-pre-command-hook)) (catch (quote top-level) (progn (eshell-trap-errors (eshell-named-command "x")))) (run-hooks (quote eshell-post-command-hook))))
  (eshell-commands (progn (run-hooks (quote eshell-pre-command-hook)) (catch (quote top-level) (progn (eshell-trap-errors (eshell-named-command "x")))) (run-hooks (quote eshell-post-command-hook))))
  eval((eshell-commands (progn (run-hooks (quote eshell-pre-command-hook)) (catch (quote top-level) (progn (eshell-trap-errors (eshell-named-command "x")))) (run-hooks (quote eshell-post-command-hook)))))
  eshell-send-input(nil)
  funcall-interactively(eshell-send-input nil)
  call-interactively(eshell-send-input nil nil)
  command-execute(eshell-send-input)

Without debug-on-error the "Wrong type argument"-message ends up in the buffer I was working in when invoking eshell. To add to my confusion, this only happens the first time I call eshell/x. Stepping through the function with edebug suggests that the error occurs in neither of the functions called inside eshell/x as the debugger only starts after I stepped over the last function call. Also when changing the function to this

(defun eshell/x ()
  (insert "exit")
  (eshell-send-input)
  (delete-window)
  (message "done deleting window"))

The debugger only gets entered after "done deleting window" appeared in the echo area.

Now I don't know how to continue searching for the error. Can someone please help me identify the error?

0

With the default settings of eshell, typing the word "exit" in the *eshell* buffer, followed by the enter key, causes Emacs to execute the function eshell/exit. As such, the proposed approach set forth in the question above, i.e., by inserting the word "exit" and calling eshell-send-input, is probably not the best way to handle this.

So, let us start by examining the built-in function eshell/exit, which is essentially a one-liner. To do that, we evaluate (require 'eshell) with something like M-: (aka M-x eval-expression) ; or, we could copy/paste the (require 'eshell) statement to a *scratch* buffer and place our cursor just after the closing parenthesis and type C-x C-e. Then, we locate the function with M-x find-function RET eshell/exit RET -- it looks like this:

(defun eshell/exit ()
  "Leave or kill the Eshell buffer, depending on `eshell-kill-on-exit'."
  (throw 'eshell-terminal t))

Since it is just a one-liner, why not create our own function by simply changing the function name, like so?:

(defun eshell/x ()
  "Leave or kill the Eshell buffer, depending on `eshell-kill-on-exit'."
  (throw 'eshell-terminal t))

We decide to pay attention to the doc-string about the variable eshell-kill-on-exit and if we are curious, we type C-h v (aka M-x describe-variable) and look it up:

eshell-kill-on-exit is a variable defined in ‘esh-mode.el’.
Its value is t

Documentation:
If non-nil, kill the Eshell buffer on the ‘exit’ command.
Otherwise, the buffer will simply be buried.

You can customize this variable.

As luck would have it, we have the benefit of a similar thread which has an accepted answer:

https://stackoverflow.com/questions/51867693/emacs-eshell-kill-window-on-exit

So, we take the answer in that similar thread -- suggesting that we advise eshell-life-is-too-much, and we combine it with our new function eshell/x:

(require 'eshell)

(defun eshell/x ()
  (throw 'eshell-terminal t))

(defun my-custom-func () 
  (when (not (one-window-p))
    (delete-window)))

(advice-add 'eshell-life-is-too-much :after 'my-custom-func)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.