This is a concept for an updated architecture for starship, which I think is worthy of further exploration and testing.
Starship is a fast prompt for most users, most of the time. However, some of its modules are
inherently I/O intensive, and this can lead to poor prompt performance under some conditions. For
example, the git_status
module can be slow to compute in very large git repositories.
Starship is also a stateless prompt. All values printed to the prompt are re-calculated every time the prompt is shown. This occurs despite the fact that most modules will produce unchanged output on subsequent prompts. In a rapid-fire terminal session, Starship mostly needs to rely on the operating system to keep recently used files in cache in order to keep performance acceptable.
The contributors to Starship have worked hard, under this stateless model, to make performance as fast as possible. However, we are increasingly approaching a ceiling of what performance is possible under the model.
Starship should, during starship init
, launch a long-running daemon. This daemon would be
responsible for generating prompts, and storing caches and other state which allows subsequent
prompts to be re-calculated much faster.
Under this new model, each module could:
- Specify how long it should be cached for (or not at all)
- Nominate files/directories to be watched for changes. Using OS filesystem watchers (e.g., through
the
notify-rs
crate), we can invalidate those caches so that the prompt remains fresh.
Other possible features could include:
- Pre-calculating prompts for frequently visited directories.
- Allow the user to manually pin directories in the starship config file
- Implement a timeout for prompt rendering, after which stale or incomplete data may be shown.
Create a new verb for the starship CLI, daemon
, which starts the daemon. Use a lockfile (e.g.,
starship.lock
) in the user's home directory, or adjacent to the starship config file, to ensure
the daemon is only run once per user. The lockfile
crate may be suitable.
If the daemon is not already running, then starship daemon
starts and detaches the daemon, and
returns on stdout some socket address which can be used to communicate with the daemon. This is
likely to be the address of some simple, platform-specific, high performance socket: such as a unix
socket on MacOS and Linux, or a named pipe on Windows.
If the daemon is already running, then starship daemon
returns the socket address of the already
running daemon. Perhaps this address is written to a file (e.g., starship.socket
) after the
lockfile is successfully acquired.
The starship init
verb should be similar to the existing implementation, but the shell-specific
init scripts should be updated to include the following behaviours:
-
Invokes
starship daemon
, and store the socket address returned in some shell variable -
Parameterize the shell's prompt function to take the socket address as an argument to
starship prompt
.- e.g.,
starship prompt --daemon-address=abcd --status=x --cmd-duration=y --jobs=z
- This prevents us from having to do a fresh lookup of the socket address each time we invoke
starship prompt
. - If
--daemon--address
is not passed as an argument tostarship prompt
, we can fall back to loading this from thestarship.socket
file.
- e.g.,
-
Attaches the current shell to the daemon, by passing the PID of the shell process to the daemon.
- We might achieve this through an argument to
starship daemon
. e.g.,starship daemon --attach-shell 1234
. - The daemon can regularly poll the OS to ensure each attached shell process is still running. Shells which exit are removed from the attached shell list. If all shells exit, then the daemon itself can exit.
- Some users may use
screen
ortmux
for very long-lived terminal sessions, so perhaps we should also have an idle timeout (e.g., no prompts generated in 3 hours) to kill the daemon.
- We might achieve this through an argument to
We could support a daemonless mode, equivalent to the existing stateless starship behaviour, by
supporting a --daemonless
argument to starship init
.
The existing prompt
verb will defer most of it's current work to the daemon. It captures the last
exit code, command duration and job count - and sends these along with the current directory path to
the daemon via the socket. The daemon responds with a fully-rendered prompt.
If the prompt detects that the daemon is unavailable, then it should attempt to re-initialize
starship. This could involve some special exit code from starship prompt
, which is then detected
by the shell-specific prompt scripts.
RE: Process tracking - this might be worth looking at: https://natanyellin.com/posts/tracking-running-processes-on-linux/