From fc828295256a7319730b53da694e7d520c235973 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Sun, 6 Aug 2023 19:20:37 -0700 Subject: [PATCH] yeet zim post --- content/posts/zsh-zim-setup.md | 270 +++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 content/posts/zsh-zim-setup.md diff --git a/content/posts/zsh-zim-setup.md b/content/posts/zsh-zim-setup.md new file mode 100644 index 0000000..e4045c4 --- /dev/null +++ b/content/posts/zsh-zim-setup.md @@ -0,0 +1,270 @@ ++++ +date = "2023-08-06" +draft = false +path = "/blog/zsh-zim-setup" +tags = ["zim", "zsh", "workflow"] +title = "Setting up zim with zsh" ++++ + +I have been using an unmaintained fork of [prezto] since 6 years ago, which has +been mostly completely fine. However, one day (and possibly due to my own +actions), it started having severe startup performance issues on my NixOS +system one day, and enough was enough: I will finally figure out how zsh works. +It would be unfair to blame `prezto` for the startup performance issues, since +the version in question is so old. + +[prezto]: https://github.com/sorin-ionescu/prezto + +## What's broken anyway? + +It's possible to profile zsh execution [using `zprof`][zprof]. + +[zprof]: https://docs.jade.fyi/zsh/zsh.html#The-zsh_002fzprof-Module + +To do this, put `zmodload zsh/zprof` into the top of your `.zshrc`, and then +`zprof` at the end of it. + +This will print out a listing of where the time went: + +``` +num calls time self name +----------------------------------------------------------------------------------- + 1) 2 36.07 18.04 58.27% 10.75 5.37 17.36% pmodload + 2) 2 9.71 4.86 15.69% 9.71 4.86 15.69% compaudit + 3) 2 5.52 2.76 8.92% 5.52 2.76 8.92% zle-reset-prompt + 4) 5 5.01 1.00 8.09% 4.06 0.81 6.55% _zsh_highlight_main_highlighter + +``` + +This output is from another machine not as bad as my laptop; I found that the +time went into `pmodload` in prezto, which is a function that sources modules; +not terribly interesting. + +Undoubtedly, it would have been possible to fix the ancient prezto fork, but it +needed rewriting anyway. + +## Rewrite it + +Since I'm rewriting it anyway, I switched to [zim], a fancy zsh module manager +thingy. It's distinctly possible that I might wind up canning zim in the future +due to opinions about software pinning, but at least it's much much smaller and +easier to delete. + +In particular, zim itself is merely a downloader, and is *not even run* on +normal shell startup if nothing has changed, which means that I can throw away +their code any day. + +[zim]: https://github.com/zimfw/zimfw + +## Values + +I don't want to have dependencies ever update unexpectedly, and I want my +environment to be exactly the same on different machines. Automatic updating +for such a critical tool as my shell configuration, which *does not break +itself* and for the most part never actually needs updates is highly +undesirable. + +`zim` does not really agree with this view, but it can be made to work with it +well enough. + +## What zim does + +`zim` does two things: it downloads dependencies for you, and it compiles a +static `init.zsh` file that is loaded on shell startup. The dependency +downloading is done via either GitHub tarballs or `git`, and is invoked by +`zimfw install` (and `zimfw init`). + +The installation process skips directories that already exist (!). + +`zim` compiles a static file `init.zsh`, which is sourced on shell startup. It +looks something like the following: + +```bash +zimfw() { source /home/jade/.local/share/zim/zimfw.zsh "${@}" } +zmodule() { source /home/jade/.local/share/zim/zimfw.zsh "${@}" } +fpath=(/home/jade/.local/share/zim/modules/prompt-pwd/functions /home/jade/.local/sh +are/zim/modules/git-info/functions /home/jade/.local/share/zim/modules/utility/funct +ions /home/jade/.local/share/zim/modules/zsh-completions/src ${fpath}) +autoload -Uz -- prompt-pwd coalesce git-action git-info mkcd mkpw +source /home/jade/.local/share/zim/modules/utility/init.zsh +source /home/jade/.local/share/zim/modules/input/init.zsh +source /home/jade/.dotfiles/configs/zsh/prompt.zsh-theme +# +``` + +## Making zim work + +The dependency management strategy I used for zim is `git subtree` vendoring. +The reason I am doing it this way is that it ensures that zim is the exact +selected version and avoids submodules. + +Since `zimfw install` ignores already-existing directories, we can simply +check in the entire `modules/` directory inside `ZIM_HOME` after running `zimfw +install` and deleting `modules/` from the gitignore. This works much better if you +use `degit`, the GitHub-tarball downloader, since otherwise you would have +nested git repos. + +`zim` also chooses to attempt to update itself monthly via the Internet, so +that needs to go too. + +This leads to the following zim configuration in `.zshrc`, prior to sourcing +`${ZIM_HOME}/init.zsh`: + +```bash +zstyle ':zim:zmodule' use 'degit' +zstyle ':zim' 'disable-version-check' 'true' +``` + +Then just run `zimfw install` and check in the modules. Now it's pinned, and +`zimfw update` will update the pins. + +## Completion + +`zsh` has a very advanced completion system, which I have years-old muscle +memory with, so I am going to be tweaking my config until it feels right again. + +Settings for completions are under `zstyle +':completion:func:completer:command:argument:tag' 'somestyle'`. Not all of +these fields have to be used; globs can be used. For more details, see +[Completion System Configuration]. + +[Completion System Configuration]: https://docs.jade.fyi/zsh/zsh.html#Completion-System-Configuration + +For example, the following will set the `completer` setting for all contexts +to a configuration which does the following: +- `_extensions`: if the cursor is at `f._`, it will try to complete a file + extension. +- `_complete`: invoke the standard completion system. +- `_match`: expands globs into the applicable completions when tab is pressed + ^(\[citation needed\]). +- `_approximate`: tries to autocorrect things to a similar completion; fixes + paths and other misspelled things. + +`zstyle ':completion:*' completer _extensions _complete _match _approximate` + +To find out where the completion system is getting ideas from, invoke +[`_complete_help`][complete-help] by pressing `C-x h`: + +[complete-help]: https://docs.jade.fyi/zsh/zsh.html#index-_005fcomplete_005fhelp-_0028_005eXh_0029 + +``` +dev/zlog ยป ls new* +tags in context :completion::approximate::: + corrections original (_approximate) +tags in context :completion::approximate-1:ls:: + argument-rest options (_arguments _ls) +``` + +## History + +By default zsh does not save history! Also, there are various options that +probably should be changed: + +``` +# save a lot of history +HISTSIZE=1000000 +SAVEHIST=1000000 + +# all instances share the same history +setopt SHARE_HISTORY + +# history expansion goes into the editor buffer first +setopt HIST_VERIFY + +# don't show dupes in history search +setopt HIST_FIND_NO_DUPS + +# don't history commands beginning in space (consistent with bash) +setopt HIST_IGNORE_SPACE + +# allow comments in the shell +setopt INTERACTIVE_COMMENTS +``` + +History search is built into the shell, but it is not fully bound by default, +which is kind of odd. The following will bind, effectively, the emacs +bindings in `viins` mode such that you can `C-r` and `C-s` as in bash (I +know about the conflict with `C-r`; I don't use undo in my shell): + +```bash +bindkey -M vicmd "?" history-incremental-pattern-search-backward +bindkey -M vicmd "/" history-incremental-pattern-search-forward + +bindkey -M viins '\C-R' history-incremental-pattern-search-backward +bindkey -M viins '\C-S' history-incremental-pattern-search-forward + +unsetopt FLOW_CONTROL # disable C-s/C-q in the editor +``` + +When inside a search already, this is considered `viins` mode, so hitting `C-r` +will cycle through the previous results (it took me 6 years to fix being able +to get more than one result in history). + +You can also search history with `history 1 | grep`. + +In the future I am probably going to adopt [atuin] for nicer shell history with +better metadata and self-hosted syncing across machines. + +[atuin]: https://github.com/atuinsh/atuin + +## Syntax highlighting and fancy autosuggestions + +Alternate title: Something `fish`y going on here! + +Use `zmodule zsh-users/zsh-syntax-highlighting` to get +[zsh-syntax-highlighting], which adds highlighting in the prompt. I find this +super valuable since it highlights file names and makes it obvious before even +running a command whether the path is right. + +[zsh-syntax-highlighting]: https://github.com/zsh-users/zsh-syntax-highlighting + +You can also get the fish-like history suggestions with [zsh-autosuggestions]. + +[zsh-autosuggestions]: https://github.com/zsh-users/zsh-autosuggestions + +## Bonus: nvim integration + +Fun fact: nvim exposes a socket at `$NVIM` to subprocesses, so if you use nvim +as a terminal emulator, you can send arbitrary RPC to the editor from the shell +session. + +This is great: set `$VISUAL = 'nvr --remote-wait'` in your nvim configuration +to use [`nvim-remote`][nvim-remote] to open files in the surrounding nvim +instance. This makes `git commit`, `sudo -e`, and other things magically just +work. + +I rewrote `nvim-remote` in Rust as [nvimsplit], which is what I use; the +differences are as follows: +- Use the `BufHidden` event instead of `BufDelete`: if you have `'hidden'` set, + `nvr` will not return since it listens for `BufDelete` (which will not be + fired unless `:bde` is explicitly called, or `nvr` is invoked as `nvr + --remote-wait +'setlocal bufhidden=delete' file`). +- Handle weird file names better: `nvr` can't open files that look like + options. + +[nvimsplit]: https://github.com/lf-/dotfiles/tree/main/programs/nvimsplit + +[nvim-remote]: https://github.com/mhinz/neovim-remote + +## Future improvements + +I want to have a fuzzy file search similar to [telescope.nvim] in my shell. +This is probably the most useful one improvement I could make. One day. + +[telescope.nvim]: https://github.com/nvim-telescope/telescope.nvim + +## Finale + +My shell works again :D + +For me the shell is very much a tool that must work 100% of the time and not +something to mess with often, as may be evidenced by it having been last +seriously messed with 6 years ago. The rewrite project has been a success in +this respect: I definitely have less code around, and it is now more +maintainable. + +It would be kind of nice to switch to PowerShell or nu shell on my Linux +machines, but there's enough broken programs that assume POSIXness from +`$SHELL`, and shell replacement is not something I really want to do. + +[nushell]: https://www.nushell.sh/