Description
Is your feature request related to a problem? Please describe.
There are many programs that expect to be integrated into your shell by writing something like eval "$(theprogram init)"
into your shell initialisation script. The program generates some shell code on the fly to hook itself into the appropriate parts of your shell. Direnv, Starship, Zoxide, and Carapace all recommend this installation method, for example, as do various "version manager" tools like rbenv and Pyenv.
There are also programs like vivid which expect to be called during shell initialisation and have their output saved to a parameter (LS_COLORS
in this case), rather than directly evaluated as shell script. Expecting this seems to be less common - even similar tools like dircolors
produce executable code that sets LS_COLORS
rather than simply a value that needs to be saved to LS_COLORS
- but it does happen, and I personally use vivid so I noticed.
However this code-generation approach can be a source of slowdown, because it needs to fork and exec the external program every time your shell starts. Additionally, the Zsh code generated by the external program cannot be zcompile
d, since it is always produced on the fly. Since the actual generated code produced by these commands changes relatively rarely, it makes a lot of sense to prebuild it, zcompile
it, and source that file on startup instead. Zim doesn't have a built-in way to do this, and I think it would be nice if it did.
Describe the solution you'd like
Inspired by a similar feature in the Znap tool, Zim could provide an additional command inside the zimrc called something like zeval
, which would be invoked like this:
zeval direnv 'direnv hook zsh'
zeval zoxide 'zoxide init zsh'
When such a definition is "installed", Zim would run the provided command once, save the result to a new init.zsh
file, and zcompile
that file. Then on each launch, Zim would simply source that same init.zsh
along with everything else.
Scenarios like vivid
, where the command output needs to be saved to a parameter rather than simply evaluated,
7185
could be supported using a flag like this:
zeval vivid -p LS_COLORS 'vivid generate molokai'
When this is provided, rather than just writing the command's output directly to init.zsh
, Zim could generate something like export LS_COLORS="output of command"
and write that to init.zsh
instead.
Zim could detect when the command in zimrc has been changed (say, from vivid generate molokai
to vivid generate gruvbox-dark
) and rerun it to produce a new cached result in that case, but otherwise keep using the cached output. Additionally a zimfw
subcommand for invalidating cached evals would be useful.
Describe alternatives you've considered
I've already come up with a hacky way to implement this idea with Zim's existing features. It looks like this, in my zimrc:
zeval() {
zmodule https://git.00dani.me/00dani/null --name zeval-$1 --on-pull "$2 >! init.zsh"
}
zeval-if-installed() {
(( ${+commands[$1]} )) && zeval "$@"
}
# Installing other zmodules here in the normal way...
zeval-if-installed direnv 'direnv hook zsh'
zeval-if-installed zoxide 'zoxide init zsh'
zeval-if-installed starship 'starship init zsh --print-full-init'
zeval-if-installed vivid 'echo export LS_COLORS=${(qqq)$(vivid generate molokai)}'
zmodule completion
# Carapace calls compdef, so it needs to be sourced after compinit to work properly
zeval-if-installed carapace 'carapace _carapace zsh'
unfunction zeval zeval-if-installed
While this does absolutely work, this setup has a number of shortcomings: it's needlessly cloning an empty Git repository for every command I add, there's no simple way to invalidate the cached script when necessary, and the syntax I need to use for "save to a parameter" commands like vivid
is honestly pretty gross!
I could fix that last issue myself by teaching my zeval
function about the -p
flag I suggested above, but the other problems really need support in Zim itself to be fixed properly.
Outside of my personal hack, I've found there are also plugins like evalcache and zsh-smartcache which implement this idea as a standalone utility. However these approaches have their own shortcomings: they don't support caching of a parameter assignment (so they don't work with vivid
) , they run totally independently of your plugin manager so zimfw
naturally can't manage them, and they both unconditionally shell out to md5
or md5sum
to hash your command so you're still not actually avoiding a fork/exec by using them. Strangely, they also do not attempt to zcompile
the cached output. Not sure why.
Additional context
No response