Home

Awesome

zsh-defer: Deferred execution of zsh commands

zsh-defer defers execution of a zsh command until zsh has nothing else to do and is waiting for user input. Its intended purpose is staged zsh startup. It works similarly to Turbo mode in zinit.

Features:

Table of Contents

  1. Installation
  2. Usage
  3. Example
  4. Caveats
  5. FAQ
    1. Is it possible to autoload zsh-defer?
    2. Is it possible to find out from within a command whether it's being executed by zsh-defer?
    3. Is zsh-defer a plugin manager?
    4. How useful is it?
    5. Is zsh-defer compatible with Instant Prompt in Powerlevel10k?
    6. Can I use zsh-defer together with zinit?
    7. How does zsh-defer compare to Turbo mode in zinit?
    8. Why so many references to and comparisons with zinit?

Installation

  1. Clone the repo.
git clone https://github.com/romkatv/zsh-defer.git ~/zsh-defer
  1. Add the following line at the top of ~/.zshrc:
source ~/zsh-defer/zsh-defer.plugin.zsh

Using a plugin manager? You can install zsh-defer the same way as any other zsh plugin hosted on GitHub.

Usage

zsh-defer [{+|-}12dmshpr] [-t delay] word...
zsh-defer [{+|-}12dmshpr] [-t delay] -c list

Queues up the specified command for deferred execution. Whenever zle is idle, the next command is popped from the queue. If the command has been queued up with -t delay, execution of the command and all deferred commands after it is delayed by the specified number of seconds (non-negative real number) without blocking zle. Duration can be specified in any format accepted by sleep(1). After that the command is executed either as word... with every word quoted, or, if -c is specified, as eval list. Commands are executed in the same order they are queued up.

Options can be used to enable (+x) or disable (-x) extra actions taken during and after the execution of the command. By default, all actions are enabled. The same option can be enabled or disabled more than once -- the last instance wins.

OptionAction
1Redirect standard output to /dev/null.
2Redirect standard error to /dev/null.
dCall chpwd hooks.
mCall precmd hooks.
sInvalidate suggestions from zsh-autosuggestions.
zInvalidate highlighting from zsh-syntax-highlighting.
pCall zle reset-prompt.
rCall zle -R.
aShorthand for all options: 12dmszpr.

Example

Here's an example of ~/.zshrc that uses zsh-defer to achieve staged zsh startup. When starting zsh, it takes only a few milliseconds for this ~/.zshrc to be evaluated and for prompt to appear. After that, prompt and the command line buffer are refreshed and buffered keyboard input are processed after the execution of every deferred command.

source ~/zsh-defer/zsh-defer.plugin.zsh

PS1="%F{12}%~%f "
RPS1="%F{240}loading%f"
setopt promp_subst

zsh-defer source ~/zsh-autosuggestions/zsh-autosuggestions.zsh
zsh-defer source ~/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
zsh-defer source ~/.nvm/nvm.sh
zsh-defer -c 'RPS1="%F{2}\$(git rev-parse --abbrev-ref HEAD 2>/dev/null)%f"'

Zsh startup without zsh-defer. Prompt appears once everything is loaded.

zsh startup without zsh-defer

Zsh startup with zsh-defer. Prompt appears instantly and gets updated after every startup stage.

zsh startup with zsh-defer

  1. zsh-autosuggestions is loaded: completion suggestion appears.
  2. zsh-highlighting is loaded: nvm in the command line turns red (no such command).
  3. nvm is loaded: nvm turns green (recognized command).
  4. RPS1 is set: the name of the current Git branch appears.

Caveats

zsh-defer executes commands from zle. This has several ramifications.

zsh-defer executes commands in function scope with LOCAL_OPTIONS, LOCAL_PATTERNS and LOCAL_TRAPS options. This can break initialization scripts that use typeset without explicit -g, set options, change patterns or install traps.

FAQ

Is it possible to autoload zsh-defer?

Yes.

Instead of this:

source ~/zsh-defer/zsh-defer.plugin.zsh

You can do this:

autoload -Uz ~/zsh-defer/zsh-defer

Is it possible to find out from within a command whether it's being executed by zsh-defer?

Yes. Check whether zsh_defer_options parameter is set.

function say-hi() {
  if (( $+zsh_defer_options )); then
    echo "Hello from zsh-defer with options: $zsh_defer_options" >>/tmp/log
  else
    echo "Hello from without zsh-defer" >>/tmp/log
  fi
}

say-hi            # Hello from without zsh-defer
zsh-defer say-hi  # Hello from zsh-defer with options: 12dmshp

Is zsh-defer a plugin manager?

No. zsh-defer is a function that allows you to defer execution of zsh commands. You can use it on its own or with a plugin manager.

How useful is it?

About as useful as Turbo mode in zinit.

Is zsh-defer compatible with Instant Prompt in Powerlevel10k?

Yes. Although if you are using Powerlevel10k with Instant Prompt you almost certainly don't need to use deferred loading of any kind.

Can I use zsh-defer together with zinit?

Yes, both ways.

You can load zsh-defer with zinit the same way as any other plugin.

zinit light romkatv/zsh-defer

You can defer a zinit command with zsh-defer the same way as any other command.

zsh-defer zinit light zsh-users/zsh-autosuggestions
zsh-defer zinit light zsh-users/zsh-syntax-highlighting

How does zsh-defer compare to Turbo mode in zinit?

They are quite similar. Both allow you to defer execution of a zsh command and both execute the command from zle, with all the negative consequences that this entails.

zsh-defer is most useful to those who don't use zinit as it gives them access to Turbo mode that they otherwise didn't have. However, there are also a few minor benefits to using zsh-defer zinit light compared to the builtin Turbo mode:

Has zinit closed the gap on one or more of these points? Please send a PR removing them from the list.

On the other hand, zinit ice wait has its own advantages:

Why so many references to and comparisons with zinit?

Turbo mode in zinit is the only other robust implementation of deferred zsh command execution that I'm aware of. There is also zsh/sched but it's underpowered by comparison.

Note that zsh-defer is not a plugin manager and thus not an alternative to zinit.