Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(shell/zsh.rs): avoid hook-env execution on Enter without command #2861

Merged
merged 10 commits into from
Nov 5, 2024

Conversation

powerman
Copy link
Contributor

Some zsh configuration are intentionally optimized to work really fast and it's important to keep it feels this way. And while mise hook-env is really fast (0.008 sec on my system) and slowdown isn't noticeable when running any command (even builtin one like echo or true) it still result in noticeable slowdown when just pressing Enter without any command.

At a glance proposed optimization shouldn't break any functionality, and it fixes mentioned above noticeable slowdown.

Some zsh configuration are intentionally optimized to work really fast and it's important to keep it feels this way. And while `mise hook-env` is really fast (0.008 sec on my system) and slowdown isn't noticeable when running any command (even builtin one like `echo` or `true`) it still result in _noticeable_ slowdown when just pressing Enter without any command.

At a glance proposed optimization shouldn't break any functionality, and it fixes mentioned above noticeable slowdown.
Fixes an issue when directory is changed after empty command (just Enter) using keyboard shortcut (e.g. Ctrl-PgUp/Down) instead of `cd` command - in this case hook wasn't triggered which result in showing other project's env variables to first command executed in non-project dir.
This fixes two cases:
- Optimization bug: first command in a new shell won't get right env.
- Adds support for `zsh -i -c '...'` which also won't get right env.
src/shell/zsh.rs Outdated
@@ -20,6 +20,8 @@ impl Shell for Zsh {
export MISE_SHELL=zsh
export __MISE_ORIG_PATH="$PATH"

eval "$({exe} hook-env{flags} -s zsh)"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes two issues:

  • One is related to this PR: first command in a new zsh won't get right env because of this PR's optimization.
  • Another issue is not related to this PR and fixes zsh -i -c '...' - current mise implementation won't provide right env to the command executed inside child zsh.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember why but there is a reason I chose not to run hook-env automatically

Copy link
Contributor Author

@powerman powerman Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember why but there is a reason I chose not to run hook-env automatically

Sometimes only way to recall it is to change a code and wait until something breaks.

I'm not sure is missing env in a zsh -i -c '...' considered a real issue. This is probably a real "corner" case and might not worth supporting: without -i it won't load Mise anyway, but -i with -c makes not much sense and in my experience it used mostly for testing zsh performance - how fast it loads user configs in interactive mode.

Still, your doc example in https://mise.jdx.dev/dev-tools/shims.html#zshrc-bashrc-files ask user to run it manually after activation - which has exactly same effect as my change to run it automatically but require extra manual change in user's config. Also, same 2-command example is repeated in https://mise.jdx.dev/troubleshooting.html#mise-isn-t-working-when-calling-from-tmux-or-another-shell-initialization-script.

So, I'm not joking here: if you can't remember why you chose not to run hook-env automatically then in current situation I think it makes sense to start running it automatically - this way it either will work and let you simplify UX and clean up docs, or breaks and this time you'll add a comment somewhere describing why it's bad idea to run it automatically on activation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this was the reason: #204 (comment)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that dotenv conflict is still actual, then you probably need to mention needs to manually add second line with hook-env in shell rc files at https://mise.jdx.dev/getting-started.html#_2-activate-mise (and maybe with a note about direnv and what to do between these two lines to avoid conflict with dotenv.

@powerman
Copy link
Contributor Author

Also, if mise activate zsh will now output code which runs mise hook-env, then this doc should be updated: https://mise.jdx.dev/dev-tools/shims.html#zshrc-bashrc-files

@jdx jdx merged commit a1cdc4b into jdx:main Nov 5, 2024
12 checks passed
jdx added a commit that referenced this pull request Nov 6, 2024
@joshbode
Copy link
Contributor

joshbode commented Nov 6, 2024

It could be my particular setup, but I'm finding that the chpwd and precmd hooks are both executing whenever I change directory (in that order), so I've replaced the _mise_hook in my .zshrc as follows to ensure they're mutually exclusive:

source <(mise activate zsh)

function _mise_hook {
  source <(mise hook-env -s zsh)
  ZLAST_COMMANDS=""  # prevent precmd hook also triggering
}
ZLAST_COMMANDS="_"  # ensure the precmd hook executes initially

@powerman
Copy link
Contributor Author

powerman commented Nov 6, 2024

ZLAST_COMMANDS="_"  # ensure the precmd hook executes initially

I think it's better to do eval "$(mise hook-env -s zsh)" instead here (after mise activate) - this way is mentioned in the docs, it doesn't mangle ZLAST_COMMANDS and it doesn't makes any assumptions about current hook optimizations.

  ZLAST_COMMANDS=""  # prevent precmd hook also triggering

This may breaks other things which use ZLAST_COMMANDS. So, it's more safe to use own flag instead:

+typeset -g mise_skip_hook_cmd
 _mise_hook() {
   eval "$(mise hook-env -s zsh)";
+  mise_skip_hook_cmd=1
 }
 _mise_hook_cmd() {
-  if [[ -n "$ZLAST_COMMANDS" ]]; then
+  if [[ -n "$ZLAST_COMMANDS" && -z "$mise_skip_hook_cmd" ]]; then
     _mise_hook;
   fi
+  mise_skip_hook_cmd=
 }

If you like to avoid this extra complexity then this one will works too in most (but not all) cases:

-  if [[ -n "$ZLAST_COMMANDS" ]]; then
+  if [[ -n "$ZLAST_COMMANDS" && "$ZLAST_COMMANDS" != "cd" ]]; then

@joshbode
Copy link
Contributor

joshbode commented Nov 6, 2024

Good point, @powerman

I ended up with this solution, which seems to work well for me, and doesn't rely on ZLAST_COMMANDS (I don't always use cd to change dirs, sometimes just directly typing the directory name as I have the AUTO_CD option enabled):

_last_mise_hook_trigger=""

function _mise_hook_chpwd {
  _mise_hook
  _last_mise_hook_trigger="chpwd"
}
chpwd_functions[1]=_mise_hook_chpwd

function _mise_hook_precmd {
  if [[ ${_last_mise_hook_trigger} == "precmd" ]]; then
    _mise_hook
  fi
  _last_mise_hook_trigger="precmd"
}
precmd_functions[1]=_mise_hook_precmd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants