True. I'll have to update the documentation with some examples (and/or make a video?).
I know you know them but I list the 5 modes quick here for readers:
- semi-char: The default, where most keys go to the terminal but some common Emacs prefixes (M-x, C-c) go to Emacs
- char-mode: ALL keys go to the terminal. This way you can run e.g. Emacs inside Emacs.
- copy-/emacs-mode: This makes the whole buffer a pure read-only Emacs buffer and all keys go to Emacs. The difference between copy and emacs-mode is that copy freezes the terminal output (comes originally from vterm), while in emacs-mode new output keeps coming in (adopted from eat).
- line-mode: It's like `M-x shell`, everything goes to Emacs but it's not read only but you can type text on the prompt. But nothing is send to the terminal until you press return.
> why is nvim +term has only two modes, but this one has 5
I haven't used the neovim terminal, but I guess you can compare vim insert-mode with semi-char and normal-mode with copy-mode. And surely they have a char-mode as well, or can you not run nvim inside nvim (without having to press some quote key all the time)?
That would leave only line-mode as the odd one out.
> But it's not super clear how in practice use that leverage efficently
As with all Emacs things, that's highly personal.
Currently, I use semi-char mostly and switch to copy-mode to select/copy stuff.
For all Emacs package updates, it's best of course to check the source what's changed and then you can compile yourself.
PS, I also think Emacs is one of the few ecosystems where people actually
check what changed. It's not like npm where you have a million of deps unreviewed pulled in.
Personally I use borg and always at least quick check what changed, and most Emacs
package managers have a similar feature.
Fwiw, in all my Ghostel updates recently, the Zig module failed to compile, with an error message from Zig about a line in the module source. I have a fairly up to date Zig installed via Homebrew.
I didn't have time to fix it so downloading the binary module has been the only option.
I had the same problem with vterm when I first tried it. The C module failed to compile, with a compiler error about a line in the source. As there was no downloadable binary module I fixed that one.
Just want to add that we have the same feature in Ghostel.
As it copies stuff to a remote host, automatic ssh injection is disabled by default but you can enable it by setting `ghostel-tramp-shell-integration` to true.
And not only mouse click of course, but you can use `ghostel-previous-hyperlink` (or ghostel-next-hyperlink) to quickly jump to the last hyperlink displayed.
It also installs a repeat-mode map, so if you see 3 url or file links as
output you can just `C-c C-p p p RET` and it will open the first link.
And I would also prefer if the link went to https://github.com/dakra/ghostel
instead of the documentation which is not that helpful if you don't know what the project is.
But for me personally, it has not only replaced vterm/eat but also any
other external terminal like kitty/Ghostty.
Having your terminal text just like a normal Emacs buffer opens up so many
possibilities and extension points that are just not available on any other terminal.
Even simple stuff like searching in the scrollback, then navigating and selecting+copying a paragraph only with the keyboard.
For every Emacs user that's so natural and fast in Ghostel while often cumbersome in other Terminals where I just reach to the mouse because it's easier.
Happy to answer any questions and also like to hear feedback positive or negative.
If you're an Emacs user and tried Ghostel and are still using Ghostty (or another external Terminal),
is there something Ghostel is missing or is it just because you want some processes to run outside of Emacs?
baokaola and I are also very active on GitHub, so feel free to open an issue if you have any.
I think the thing that stops me from running more processes inside emacs is that a large diff parse from magit or similar can block everything, and so I end up keeping a separate terminal around anyway.
Huh is that true? Does running a command in a terminal buffer block all of EMacs? That's extremely surprising, the main use of these kinds of integrated terminals for me is to compile and run which means long running commands. Is this use case fundamentally not supported due to EMacs's architecture?
I'm confused, what emacs do you have in mind? For me, emacs is a lisp machine, hackable live, M-. all the way down, that kind of things, that's the whole point (again, subjective opinion, and I'm interested in yours).
What language do you have in mind?
If you're worried about performance, Common Lisp is a compiled language. It's about the best performing dynamic language there is, and not too far behind Zig, Rust and the likes.
Fair enough. FWIW, I know several lifelong Emacs users who never wrote or read a line of lisp.
But for those who do choose Emacs for its supreme hackability, I got a hunch that their profile is very similar to those who choose lisp for its unmatched meta-programming ; they're also very similar to the OG lispers who actually rejected M-expression, the normal, more mathy syntax with less parens, and embraced S-Expressions, precisely because (= code CST AST) is what gives lisp its simplicity and powers in the first place. It's not that we are stuck with parens anyway, the initial vision of lisp, with far fewer parens, has been implemented in Rhombus for example; and we don't even have to change language, 1 or 2 days of writing macros is all it takes to get your favorite lisp to understand many expressions without brackets, and even remove many brackets from existing code; for example, I'm using something close to
https://www.reddit.com/r/lisp/comments/18b6hf/comment/c8d8wt...
to write your example as (Too $ many $ god $ damn $ brackets), which btw, doesn't use any less brackets in an Algol language: Too(many(god(damn(brackets()))))
(but yeah, in many circumstances, we do use more).
This is why I have a hard time imagining an "emacs spawn" not written in lisp. Before even considering that many Emacs concepts are most easily transcribed in a lisp, who would be interested in writing and using it in the first place? (genuine question)
eshell allows me to manipulate text as I would in any other Emacs buffer. If I have a function which wraps a word in quotes, and bind it to a key, I can be confident it will work in eshell like it does anywhere else. It's a real killer feature. If I use evil-mode, or xah-fly-keys, or simply want to use ispell to correct the spelling of a word, it all works.
Unfortunately with Ghostel none of this works. It's not integrated in the same way. There are extensions like evil-ghostel-mode, but they are limited.
Are there any plans to improve this, or is it a limitation Ghostel has to live with?
A Ghostel equivalent of eat-eshell-mode would be amazing.
Did you see ghostel line-mode?
This basically gives you a `M-x shell` experience where everything is a buffer and nothing is send to the terminal until you press enter.
There you could type on the prompt line and then call jinx or your quote wrapping function etc as it's just a normal Emacs buffer.
You can't edit the scrollback buffer though, but I don't think that's possible in eshell either.
But line-mode has it's own set of problems. Since we don't send anything to the shell, you could have some problems with autocomplete or similar things that change the text depending on each typed char.
Similarly we automatically disable line-mode when you enter a TUI (alt-screen) app, as line-mode doesn't make too much sense in e.g. vim.
But that's configurable and you can still force line-mode, it really depends on the TUI apps.
We try to support as much as possible and work around things like fish autocomplete etc. But please try and report any issues you find.
Ah. I tested it before but now I see that I have
`(setq eshell-scroll-to-bottom-on-input 'all)` in my config which always snapped point back to the prompt when I tried to edit the scrollback.
Anyway, unfortunately that is not possible in a Ghostel buffer and most likely also will never be.
I'm open to ideas though how we could improve or replicate your eshell workflow.
But also, eshell is awesome and Ghostel is not a replacement for it.
It's more a replacement for term.el, maybe shell.el (with line-mode) and other terminal packages like eat and vterm.
I think issue #426[0] would be sufficient, since it would allow for editing after the command is finished. I understand that it probably won't happen any time soon, but embedding a full terminal emulator in Eshell is the thing that's got me staying on EAT right now.
I didn't even think of the use-case that then you can edit output from finished commands in the scrollback. But it makes sense and is another +1 for that feature request.
Awesome project. Been using with doom for a while. How do you manage to get scrolling programs to work (eg Lazygit or Reasonix) where other emulators fail? Is it something special in your implentation or library that makes this work?
Are you thinking about using the scroll wheel of the mouse to scroll within the program? If the appropriate terminal modes are enabled by the application running in the terminal, we capture the scroll events in Emacs and forward them to the terminal instead of letting Emacs handle them.
Yes, that's what I mean. That's interesting because I could never get it to work properly on vterm. I suspected maybe a better base with ghosttylib might be fixing things.
Two things:
- defined f1 to toggle between semi-char vs copy modes
- I ask myself "Is it ok if Emacs dies or I nuke it?" If not, then I execute a command in the normal terminal
> defined f1 to toggle between semi-char vs copy modes
this is useful anyway but just in case you didn't know, we automatically switch to copy-mode when it makes sense.
E.g. when you activate mark, click somewhere with the mouse or when point leaves the current prompt position.
That means that with isearch or consult-line etc you're automatically in copy-mode (because why else would you search something and then jump to that position).
For other commands like avy or flash jump packages I have this in the documentation:
;; A package that runs a hook after jumping (e.g. flash):
(add-hook 'flash-after-jump-hook #'ghostel-maybe-leave-input)
;; A package without one (e.g. avy) — advise its jump action:
(with-eval-after-load 'avy
(advice-add 'avy-action-goto :after #'ghostel-maybe-leave-input))
Personally I use copy mode a lot but I rarely have to "manually" activate the mode.
Commenting late, but just want to thank you for this. I've been using it for a bit now, replacing vterm for my setup. I'll be sure to submit feedback via github.
You have the full power of Emacs to your disposal.
I have never used WezTerm but from a quick look, it seems you can quickly copy text ace-jump style.
In Emacs you could use e.g. avy to do the same, but there is probably a bunch of other similar packages that do the same.
And additionally you're not restricted to just select/copy a word. You can freely move around and use e.g. expand-region or similar to quickly select more.
It's Emacs after all, the possibilities are endless ;)
How was integration for you guys? Was the integration easier for you guys since you have an established emacs system consuming terminal output with reasoned semantics over what goes where for existing subsystems (rendering, osc codes, etc)?
For libghostty-vt, since you're targeting a terminal TUI instead of an external subsystem (for example; for ghostty, you hit libghostty-vt -> GPU rendering, which is external), you still have to buy into terminal semantics. in my experience, since I was trying to replicate mosh with libghostty-vt as the parser, what happened was that my optimized re-rendering kept getting increasingly coupled to terminal semantics (and the UDP state update model too), otherwise I'd have to send the entire terminal grid over the network like, every time.
What are the tricks for making this both performant and not like, utter cancer? You have a harder issue here too (similar to tmux) in that certain optimizations are just not available to you, or you have to translate (literally geometrically) certain instructions
I'm not entirely sure what you're asking. But I'll answer to the best of my abilities:
For Ghostel, libghostty-vt is the source of truth and the architecture is essentially that we serve input to the PTY and the PTY serves output to libghostty-vt which builds out the state in the form of a terminal screen structure. The goal is then to keep the contents of an Emacs buffer up to date to this terminal screen without replace the entire thing every time we redraw. We make use of mainly two things in order to do as little work as possible:
- Scrollback is immutable and thus never has to be modified unless it's evicted, alt screen is activated, dimensions change etc.
- libghostty-vt maintains row level dirty flags that we scan to make sure we're only replacing lines that have actually changed.
So for the rendering part, we're only diffing the grid state against the buffer, not doing anything based on terminal semantics per se, parser events that draw to the screen are passed straight to the terminal handler. But of course, certain things we need to hook into such as directory and title changes, of clipboard events etc.
Might also add that we're using the direct Zig API, not the C API, which means we have access to things that aren't exposed in the C API.
Besides the performance and what baokaola already mentioned some things that make Ghostel better than vterm especially when working with those cli agents are:
- proper handling of resizing windows
- progress reports (you get a spinner in the modeline when claude thinks)
- notifications (get an alert when your agent is done)
I know you know them but I list the 5 modes quick here for readers:
- semi-char: The default, where most keys go to the terminal but some common Emacs prefixes (M-x, C-c) go to Emacs
- char-mode: ALL keys go to the terminal. This way you can run e.g. Emacs inside Emacs.
- copy-/emacs-mode: This makes the whole buffer a pure read-only Emacs buffer and all keys go to Emacs. The difference between copy and emacs-mode is that copy freezes the terminal output (comes originally from vterm), while in emacs-mode new output keeps coming in (adopted from eat).
- line-mode: It's like `M-x shell`, everything goes to Emacs but it's not read only but you can type text on the prompt. But nothing is send to the terminal until you press return.
> why is nvim +term has only two modes, but this one has 5
I haven't used the neovim terminal, but I guess you can compare vim insert-mode with semi-char and normal-mode with copy-mode. And surely they have a char-mode as well, or can you not run nvim inside nvim (without having to press some quote key all the time)?
That would leave only line-mode as the odd one out.
> But it's not super clear how in practice use that leverage efficently
As with all Emacs things, that's highly personal.
Currently, I use semi-char mostly and switch to copy-mode to select/copy stuff.
Very rarely do I use char or line-mode.