Eshell as gymnasium
Did you note how underpowered is Eshell out of the box? There are almost no premade commands above and beyond what we'd expect from any comint mode, even in em-extras or the ELPAs… we have to write the features we want ourselves!
As it is, Eshell (and shell-mode and other comint modes) has been special only to the minority of the Emacs community who hold a lot of skill in Elisp. For the rest, it's just a weird sort of alternative to their terminal emulator, with some ups and some downs.
Yet… it has the potential to do a lot for the newbie. If you approach it a certain way, it works as a stepping stone to general Emacs-fu. Terminal emulators have ruined our mindsets, and continuing to rely on the likes of ansi-term (or using shell-mode as if it was no different from a terminal)1 hinders developing effective ways to control your system, and as a consequence, hinders developing Emacs-fu too.
Priorities in our approach:
- generalize skills
- Why can I manipulate text in Eshell with
grep
,sort
,uniq
etc, but not outside Eshell? Unix tools like these live in silos.- Even with the lovely command
objed-ipipe
, I'm just carrying the Unix silo with me, which has to be set up akin to a trebuchet instead of integrating naturally with my work.
- Even with the lovely command
- Why can I manipulate text in Eshell with
- get out of the shell
- Always try to find a way to do something that would work outside the shell too.
- avoid memorizing redundant ways to do similar things
Consider the coreutils mostly obsolete (Never learn Bash):
- We shouldn't need ls when we have dired
- We shouldn't need cd when we have find-file
- We shouldn't need grep when we have keep-lines/flush-lines
- We shouldn't need find when we have multi-occur
- and so on
Pretend you never learned the coreutils. How do you solve your next problem? There's usually a way, and it'll be a trick you can reuse!
My learning loop has been to solve a specific problem, then generalize it.
- First I wrote a command
my-copy-region-into-variable
. Then I implemented automatic backreferences: the outputs of every shell command is automatically saved into an Emacs-global variable with no effort on my part. - First I wrote a command
my-replace-path-at-point-with-truename
. Then I wrote the more generalmy-cycle-path-at-point
. - First I wrote a command
my-replace-var-at-point-with-value
which only looked up envvars like $DOOMDIR, extended it to fall back on Lisp vars if available, then realized that this is justcrux-eval-and-replace
plus envvars, so I instead extended that to also "eval-and-replace" envvars.- Then it turned out there's no reason to bind my custom version of
crux-eval-and-replace
to a key: instead, you can make it so that after every vanillaeval-last-sexp
oreval-defun
, the evalled thing is marked (without moving point) and its result copied into your kill ring, so you can simply callyank
if you wished to replace. You can additionally ensure that any motion pops the mark and restores point and any active region, so there's no interference with the mark workflow you may have had going.- An alternative to activating the region is to keep the distinction between eval and eval-print, make the result print after point (i don't see the rationale for the default behavior of printing before point, it doesn't seem to have any workflow in mind), and use C-w optionally to remove the original sexp (it works without an active region).
- Then it turned out there's no reason to bind my custom version of
- First I wrote a command
my-insert-other-buffer-file-name
. It's useful many times, but sometimes doesn't insert the buffer I had in mind. Entermy-insert-other-buffer-file-name-and-cycle-repeat
. - …