A Better Shell
I want a better shell.
There are exciting projects to improve data-processing capabilities of shells, like nushell.
However, I personally don’t use this capability of shell a lot: 90% of commands I enter are simpler than
some cmd | rg pattern.
I primarily use shell as a way to use my system, and it is these interactive capabilities that I find lacking. So I want something closer in spirit to notty.
Things I Need
The most commands I type are
I also type
mg, which launches a GUI version of Emacs with Magit:
These tools make me productive. Keyboard-only input is fast and “composable” (I can press up to see previous commands, I can copy-paste paths, etc). Colored character-box based presentation is very clear and predictable, I can scan it very quickly.
However, there are serious gaps in the UX:
ctrl+c doesn’t work as it works in every other application.
I launch GUI version of Emacs: the terminal one changes some keybindings, which is confusing to me. For example, I have splits inside emacs, and inside my terminal as well, and I just get confused as to which shortcut I should use.
The output of programs is colored with escaped codes, which are horrible, and not flexible enough. When my Rust program panics and prints that it failed in
my_crate::foo::barfunction, I want this to be a hyperlink to the source code of the function. I want to
catimages and PDFs in my terminal (and html, obviously).
My workflow after I’ve done a bunch of changes is:
cargo testto launch tests
- type ctrl+shift+Enter to split the terminal
mgin the split to start making a commit in parallel to testing
The last step is crazy!
cargo test is being run by my shell (fish), the split is handled by the terminal emulator (kitty), which launches a fresh instance of fish and arranges the working directory to be saved.
As a user, I don’t care about this terminal/terminal emulator/shell split.
I want to launch a program, and just type commands.
cargo test blocks my input?
Why can’t I type
cargo test, Enter,
exa -l, Enter and have this program to automatically create the split?
magit awesome, I want an option to use such interface for all my utilities.
And, when I type
cargo test --package, I really want completion for the set of packages which are available in the current directory.
What I really want is an extensible application container, a-la Emacs or Eclipse, but focused for a shell use-case. It could look like this:
- A GUI application (which draws using raw OpenGL: we won’t be using native OS GUI widgets).
- A UI framework for text-based UIs, using magit as a model. ctrl+c, ctrl+v and friends should work as expected.
- A tilling frame management, again, like the one in Emacs (and golden-ratio should be default).
- Some concept of process-let, which can occupy a frame.
- A prompt, which is always available, and smartly (without blocking, splitting screen if necessary) spawns new processlets.
- An API to let processlets interact with text UI.
- A plugin system for in-process processlets (obviously, plugins should be implemented in WASM).
- A plugin marketplace (versions, dependencies, lockfile, backwards compatibility).
- A plugin system for out-of-process processlets (JSON over stdio?).
- A backwards compatibility wrapper to treat usual Unix utilities as processlets.
Isn’t it Emacs that I am trying to describe? Well, sort-of. Emacs is definitely in the same class of “application containers”, but it has some severe problems, in my opinion:
- Emacs Lisp is far from the best possible language for writing extensions.
- Plugin ecosystem is not really dependable.
- It doesn’t define out-of-process plugin API (things like hyperlinking output).
- Async support is somewhere between non-existent and awkward.
- Its main focus is text editing.
- Its defaults are not really great (fish shell is a great project to learn from here).
- ctrl+c, ctrl+v do not work by default, M-x is not really remappable.
Random Closing Thoughts
This post contains the best plugin diagram ever:
This talk echoes similar sentiments:
If you build some like this, please sign me up!
A “terminals are a mess” story from today.
I wanted “kill other split” shortcut shortcut for my terminal, bound to ctrl+k, 1.
Implementing it was easy, as kitty has a nice plugin API.
After that I’ve realized that I need to remap
kill_line from ctrl+k to ctrl+shift+k, so that it doesn’t conflict with the ctrl+k, 1 chord.
It took me a while to realize that searching for
kill_line in kitty is futile — editing is handled by the shell.
Ok, so it looks like I can just remap the key in fish, by
bind \cK kill_line, except that, no, ctrl shortcuts do not work with Shift because of some obscure terminal limitation.
So, let’s go back to kitty and add a ctrl+shift+k shortcut that sends
^k to the fish!
An hour wasted.