No debug on errors in Eshell
And while we are at it, let's leave more notes for myself. This is why I like literate programming for my Emacs Configuration files.
This commit is contained in:
		
							parent
							
								
									2d9725290b
								
							
						
					
					
						commit
						1022f21d29
					
				
					 1 changed files with 60 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -31,17 +31,28 @@ Tell straight to use the built-in =eshell=:
 | 
			
		|||
#+begin_src emacs-lisp
 | 
			
		||||
  (use-package eshell
 | 
			
		||||
    :straight (:type built-in)
 | 
			
		||||
    :hook (eshell-mode . (lambda () (setq mode-line-format nil))))
 | 
			
		||||
    :hook (eshell-mode . 'ha-eshell-setup))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
After reading [[https://xenodium.com/my-emacs-eye-candy/][this essay]], I decided to try hiding the mode line in eshell windows … at least, until I get the mode line to display more important information. Note that hiding the mode line is fairly straight-forward, but others might want to use the [[https://github.com/hlissner/emacs-hide-mode-line][hide-mode-line]] package that turns that /mode-line definition/ into a minor mode that can be toggled.
 | 
			
		||||
 | 
			
		||||
I like =debug-on-error=, but not in Eshell, so I set this up when entering Eshell:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun ha-eshell-setup ()
 | 
			
		||||
    (make-local-variable 'debug-on-error)
 | 
			
		||||
    (setq mode-line-format nil
 | 
			
		||||
          debug-on-error nil))
 | 
			
		||||
#+end_src
 | 
			
		||||
** Navigation and Keys
 | 
			
		||||
Along with the regular Emacs keybindings, Eshell comes with some interesting features:
 | 
			
		||||
- ~M-RET~ gives you a prompt, even when you are running another command.  Since =eshell= passes all input to subprocesses, there is no automatic input queueing as there is with other shells.
 | 
			
		||||
- ~C-c C-p~ / ~C-c C-n~ jumps between command inputs using Eat.
 | 
			
		||||
- ~C-c C-t~ truncates the buffer if it grows too large.
 | 
			
		||||
- ~C-c C-r~ will move point to the beginning of the output of the last command.  With a prefix argument, =eshell= narrows to view its output.
 | 
			
		||||
- ~C-c C-o~ will delete the output from the last command.
 | 
			
		||||
- ~C-c C-f~ will move forward a complete shell argument.
 | 
			
		||||
- ~C-c C-f~ will move forward a complete shell argument, but I usually either type ~Escape~ to enter Evil mode, or just issue ~M-f~ / ~M-b~.
 | 
			
		||||
- ~C-c C-b~ will move backward a complete shell argument.
 | 
			
		||||
- ~M-S-r~ shows/selects a better history of commands (see below). Yes, the up/down arrows keys (as well as ~M-p~ / ~M-n~) scroll through this, but that is /slow/.
 | 
			
		||||
** Control-D Double Duty
 | 
			
		||||
Used to ~C-d~ exiting from a shell? Want it to keep working, but still allow deleting a character? We can have it both (thanks to [[https://github.com/wasamasa/dotemacs/blob/master/init.org#eshell][wasamasa]]):
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
| 
						 | 
				
			
			@ -77,9 +88,31 @@ On [[http://www.reddit.com/r/emacs/comments/1zkj2d/advanced_usage_of_eshell/][th
 | 
			
		|||
                                  (ring-elements eshell-history-ring)))))
 | 
			
		||||
  #+END_SRC
 | 
			
		||||
* Predicate Filters and Modifiers
 | 
			
		||||
The =T= predicate filter allows me to limit file results that have internal =org-mode= tags. For instance, =eshell= will send files that have a =#+TAGS:= header with a =mac= label to the =grep= function:
 | 
			
		||||
This is a cool and under-rated feature of Eshell. Type the following commands into an Eshell buffer for the details:
 | 
			
		||||
  - ~eshell-display-modifier-help~
 | 
			
		||||
  - ~eshell-display-predicate-help~
 | 
			
		||||
 | 
			
		||||
In short, use parens to limit the files, for instance:
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  $ grep brew *.org(T'mac')
 | 
			
		||||
  ls *.sh(*)   # List shell scripts that are executable
 | 
			
		||||
  ls *(/)      # List directories (recursively)
 | 
			
		||||
#+end_src
 | 
			
		||||
And parens with a colon character transform the filename, useful with =for=:
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  for F in *.org(:r) { mv $F.org $F.el }
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
The =T= predicate filter allows me to limit file results that have internal =org-mode= tags.
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  $ ls *.org(T'org')
 | 
			
		||||
  ha-agendas.org          ha-org-journaling.org      ha-org.org
 | 
			
		||||
  ha-capturing-notes.org  ha-org-publishing.org
 | 
			
		||||
  ha-org-clipboard.org    ha-org-word-processor.org
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
For instance, =eshell= will send files that have a =#+TAGS:= header with a =macos= label to the =grep= function:
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  $ grep brew *.org(T'macos')
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
As described in [[http://www.howardism.org/Technical/Emacs/eshell-fun.html][this essay]], to extend Eshell, we need a two-part function:
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +145,8 @@ For the first step, we have our function /called/ as it helps parse the text. Ba
 | 
			
		|||
               (insert-file-contents file)
 | 
			
		||||
               (re-search-forward ,reg nil t 1))))
 | 
			
		||||
      (error "The `T' predicate takes an org-mode tag value in single quotes.")))
 | 
			
		||||
#+END_src
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Then we need add that function to the =eshell-predicate-alist= as the =T= tag:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun ha-eshell-add-predicates ()
 | 
			
		||||
| 
						 | 
				
			
			@ -125,6 +159,7 @@ Gotta have some [[http://www.emacswiki.org/emacs/EshellAlias][shell aliases]], r
 | 
			
		|||
#+begin_src sh
 | 
			
		||||
  alias ll 'ls -AlohG --color=always'
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Note that you need single quotes (not double quotes). Also note that more than one parameter doesn’t work with aliases (to resolve that, we need to write [[Eshell Functions][a function]]).
 | 
			
		||||
 | 
			
		||||
Second, you can create/populate the alias file, [[file:~/.emacs.d/eshell/alias][~/.emacs.d/eshell/alias]] … as long as you don’t use those single quotes:
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +169,7 @@ Second, you can create/populate the alias file, [[file:~/.emacs.d/eshell/alias][
 | 
			
		|||
  alias d dired $1
 | 
			
		||||
  alias find echo 'Please use fd <pattern> <paths> instead.'
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Yeah, the variable =$*= doesn’t work as you’d expect, so use =$1= when calling Emacs functions that take one parameter).
 | 
			
		||||
For instance, while I would like to have the following, the real solution is to make functions (see [[Less and More][below for details]]).
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
| 
						 | 
				
			
			@ -141,6 +177,7 @@ For instance, while I would like to have the following, the real solution is to
 | 
			
		|||
#+end_src
 | 
			
		||||
 | 
			
		||||
Third,  you want more /control/, you can use the help:eshell/alias function, but it doesn’t honor =$1= and other parameters, so we could create conditionally create function that we add to the [[help:eshell-mode-hook][eshell-mode-hook]], for instance:
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp :tangle no
 | 
			
		||||
  (defun ha-eshell-add-aliases ()
 | 
			
		||||
    "Call `eshell/alias' to define my aliases."
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +195,19 @@ I have also had a lot of trouble getting aliases to work, for instance =dired= w
 | 
			
		|||
#+end_src
 | 
			
		||||
To work around this, I create functions instead.
 | 
			
		||||
* Eshell Functions
 | 
			
		||||
Any function that begins with =eshell/= is available as a command (with the remaining letters) Once I had a function =eshell/f= as a replacement for =find=, but the [[https://github.com/sharkdp/fd][fd]] project is better.
 | 
			
		||||
Any function that begins with =eshell/= is available as a command (with the remaining letters). For instance:
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (defun eshell/greet (&rest params)
 | 
			
		||||
    (let ((greeting (seq-random-elt
 | 
			
		||||
                     '(Hello Greeting Howdy "How's it going"))))
 | 
			
		||||
      (if params
 | 
			
		||||
          (format "%s, %s!" greeting
 | 
			
		||||
                  (propertize (car params) 'face
 | 
			
		||||
                              '(:weight bold :foreground "lightblue")))
 | 
			
		||||
        "Hello World.")))
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Not everything /has/ to be an Emacs function, as the normal executables are available. Once I had a function =eshell/f= as a replacement for =find=, but the [[https://github.com/sharkdp/fd][fd]] project is better.
 | 
			
		||||
 | 
			
		||||
Since =eshell= is an /Emacs/ shell, I try to think how to use Emacs buffers in a shell-focused workflow. For instance, use =view-file= instead of =less=, as it will show a file with syntax coloring, and typing ~q~ returns to your shell session.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1196,17 +1245,17 @@ And finally, add our new functions to [[elisp(describe-variable 'eshell-virtual-
 | 
			
		|||
    (add-to-list 'eshell-virtual-targets '("/dev/c" ha-eshell-target-engineering-notebook nil)))
 | 
			
		||||
#+end_src
 | 
			
		||||
* EAT and Eshell
 | 
			
		||||
The [[https://codeberg.org/akib/emacs-eat][Emulate a Terminal]] project provides flicker-free, perfect display, of visual commands in Eshell, eliminating one of my primary issue with using Eshell all the time.
 | 
			
		||||
The [[https://codeberg.org/akib/emacs-eat][Emulate a Terminal]] project provides flicker-free, perfect display, of visual commands in Eshell, eliminating one of my primary issue with using Eshell all the time.  (Check out Akib Azmain Turja’s [[https://emacsconf.org/2023/talks/eat/][talk at EmacsConf2023]]).
 | 
			
		||||
 | 
			
		||||
#+begin_src emacs-lisp
 | 
			
		||||
  (use-package eat
 | 
			
		||||
    :after eshell
 | 
			
		||||
    :straight (:repo "https://codeberg.org/akib/emacs-eat")
 | 
			
		||||
    :hook (eshell-load . #'eat-eshell-visual-command-mode))
 | 
			
		||||
#+end_src
 | 
			
		||||
Note: Bash integration?
 | 
			
		||||
#+begin_src sh
 | 
			
		||||
  [ -n "$EAT_SHELL_INTEGRATION_DIR" ] && source "$EAT_SHELL_INTEGRATION_DIR/bash"
 | 
			
		||||
#+end_src
 | 
			
		||||
 | 
			
		||||
Note that the =eat-eshell-visual-command-mode= also kicks off the global minor mode, =eat-eshell-mode=. The big advantage of Eat is the /three input modes/, however, in Eshell with Evil, I can just type ~Escape~ to go into Emacs Mode, and ~G A~ to return to typing Terminal commands.
 | 
			
		||||
 | 
			
		||||
* Special Prompt
 | 
			
		||||
Following [[http://blog.liangzan.net/blog/2012/12/12/customizing-your-emacs-eshell-prompt/][these instructions]], we build a better prompt with the Git branch in it (Of course, it matches my Bash prompt). First, we need a function that returns a string with the Git branch in it, e.g. ":master"
 | 
			
		||||
#+begin_src emacs-lisp :tangle no
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue