elvish icon indicating copy to clipboard operation
elvish copied to clipboard

Support transientPrompt

Open arthsmn opened this issue 1 year ago • 3 comments

What new feature should Elvish have?

Support cursor changes after a command is run, like this:

https://github.com/user-attachments/assets/3d8b494c-c552-4842-8fc9-2d1f0e95ff32

Some shells that support it are fish, cmd (through clink), powershell and bash (through ble). This can be used directly or with an external prompt like starship.

Output of "elvish -version"

0.21.0

Code of Conduct

arthsmn avatar Dec 27 '24 13:12 arthsmn

I am a new arrival to elvish, and I was curious whether it supported transient prompts (shells like bash and zsh require a lot of arcane magic that I don't understand to make it work, and I wanted to have something I could customize). A read through the documentation revealed hooks like $edit:before-readline and $edit:after-readline that seemed promising. Alas, experimentation proved that didn't work. Then I discovered $edit:rprompt-persistent, which is, in spirit, a form of transient prompt, just limited to the right side of it. While that isn't quite what I wanted, it suggested that most required machinery should already exist 🤔

So, I went and read more code. app.redraw function looks like the key place, since it drives the prompt rendering and even knows when the final render is happening. I also found out why before/after-readline callbacks didn't work: the prompt functions are not re-evaluated before the final render. In retrospect, it makes sense: in elvish they are evaluated asynchronously, which can take arbitrarily long. However, we need the abbreviated version of the prompt available instantly, so that we don't delay command execution just for the sake of aesthetics.

Which leads me to proposing the following design that would allow support for transient prompts with as little complexity and cost as possible:

  1. Add the $edit:final-prompt [^1] variable. If assigned, it should be a function similar to $edit:prompt, except that its output is used during the final prompt render. If nil (which would be the default), $edit:prompt's output is used, like it is today. This is important, because it means that people not interested in the transient prompt, pay no additional cost for it.
  2. $edit:final-prompt is always evaluated at the same time as $edit:prompt and $edit:rprompt, respecting the $edit:-final-prompt-eagerness setting. Note that it means that with high levels of eagerness it would be evaluated more often that its output is used. This is intentional, to make sure that the final prompt is always ready by the time we need to render it. This should not be a problem because, by nature the final prompt should be very short and simple, and therefore cheap to generate. I expect that in 99% of cases it would be (constantly "$ "), which is a very tiny overhead. People can further manage the overhead by controlling $edit:-final-prompt-eagerness.
  3. If final-prompt happens to be stale by the time it's needed, there are two ways we could handle it (I can argue in favor of either): a. Block until the prompt is ready. This makes most sense to me personally because there won't be an opportunity to update the prompt once the final render is done. Plus, it should happen extremely rarely, since the prompt is supposed to be extremely cheap to generate. b. Support $edit:final-prompt-stale-threshold and $edit:final-prompt-stale-transformer similar to the other two prompt types, and simply render the stale version if the updated one is not available by the time we need it. This is probably easiest to implement and is favored by the principle of least astonishment. In practice, I expect that difference between (a) and (b) would be rarely observable.

That's about it. If this approach seems reasonable, I can try to put together a PR with the implementation. Your feedback and suggestions are also very welcome 🙏

[^1]: Naming is up for discussion, no strong opinions there.

nevkontakte avatar May 03 '25 11:05 nevkontakte

@xiaq would love to hear your thoughts or suggestions. I think I'll have some spare time this weekend, so I could give it a try with the implementation.

nevkontakte avatar May 09 '25 16:05 nevkontakte

@nevkontakte thanks for your interest and your proposal makes sense with the current API - but Elvish's TUI is in a bit of an awkward state now. I'm rewriting it using a new framework and the development is in the etk branch, but unfortunately I haven't been able to spend much time on it in the last few months.

One of the main characteristics of etk is that it's much more open and the current edit: API: there's programmatic access to the underlying UI state, somewhat similar to attributes of HTML elements in the DOM API. So a feature like "final prompt" wouldn't require the addition of new APIs; the user should be able to be hook into the lifecycle of etk and mutate the prompt as they please.

xiaq avatar May 11 '25 16:05 xiaq