From ce6d203365878c899875ed1fd6fb242a634ecf17 Mon Sep 17 00:00:00 2001 From: Qubot <1445788683@qq.com> Date: Fri, 20 Oct 2023 21:14:23 +0800 Subject: [PATCH] first commit --- LICENSE | 21 ++++++++++++ README.md | 82 ++++++++++++++++++++++++++++++++++++++++++++ evalcache.plugin.zsh | 50 +++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 evalcache.plugin.zsh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65a352e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Matthew Rothenberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..85441ba --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# evalcache + +> zsh plugin to cache the output of a binary initialization command, intended +> to help lower shell startup time. + +## What it does + +There are lots of shell wrapper tools that follow the pattern of asking you to +eval a specific init command in your shell startup, for example, rbenv asks: + + eval "$(hub alias -s)" + +While this is very convenient, the reality is there is a small amount of +overhead associated with shelling out to this, and the output is almost always +actually static in all of the tools I know. So why bear this cost every time +you open a new tab in your shell? + +Instead, after you load this plugin, you can replace that same command with: + + _evalcache hub alias -s + +The first time this runs, it will cache the output of the command to a file, +which will be sourced in the future instead when it exists. + +If you update a tool and expect for some reason that it's initialization might +have changed, you can simply clear the cache and it will be regenerated. + +It also gracefully degrades to a no-op if the tool is no longer installed. + +## Benchmarks + +Some informal benchmarks from my MacBook on my .zshrc: + +| command | without | first run | subsequent runs | savings | +| ------------ | ------: | --------: | --------------: | ------: | +| rbenv init | ~65ms | ~65ms | ~8ms | 88% | +| hub alias | ~30ms | ~30ms | ~6ms | 80% | +| scmpuff init | ~24ms | ~25ms | ~10ms | 58% | + +The difference isn't huge, but can be handy in shaving down shell startup time, +especially if you use a bunch of these tools. Every millisecond counts! + +## Options + +- `$ZSH_EVALCACHE_DIR`: cache files storage, default `$HOME/.zsh-evalcache`. +- `$ZSH_EVALCACHE_DISABLE`: set to `true` if you wish to bypass evalcache. + +There is a convenience function to clear the cache called `_evalcache_clear`. + +## Installation + +### [Antigen](https://github.com/zsh-users/antigen) + +Add `antigen bundle mroth/evalcache` to your `.zshrc` with your other bundle commands. + +Antigen will handle cloning the plugin for you automatically the next time you start zsh. You can also add the plugin to a running zsh with `antigen bundle mroth/evalcache` for testing before adding it to your `.zshrc`. + +### [Fig](https://fig.io) + +Fig adds apps, shortcuts, and autocomplete to your existing terminal. + +Install `evalcache` in just one click. + + + +### [Oh-My-Zsh](http://ohmyz.sh/) + +1. Clone this repository into `$ZSH_CUSTOM/plugins` (by default `~/.oh-my-zsh/custom/plugins`) + + ```sh + git clone https://github.com/mroth/evalcache ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/evalcache + ``` + +2. Edit `~/.zshrc` to add _evalcache_ to your plugin list, + ```diff + - plugins=(...) + + plugins=(... evalcache) + ``` + +### [Zgen](https://github.com/tarjoilija/zgen) + +Add `zgen load mroth/evalcache` to your `.zshrc` file in the same function you're doing your other `zgen load` calls in. Zgen will handle automatically cloning the plugin for you the next time you do a `zgen save`. diff --git a/evalcache.plugin.zsh b/evalcache.plugin.zsh new file mode 100644 index 0000000..ecbea7c --- /dev/null +++ b/evalcache.plugin.zsh @@ -0,0 +1,50 @@ +# Caches the output of a binary initialization command, to avoid the time to +# execute it in the future. +# +# Usage: _evalcache [NAME=VALUE]... COMMAND [ARG]... + +# default cache directory +export ZSH_EVALCACHE_DIR=${ZSH_EVALCACHE_DIR:-"$HOME/.zsh-evalcache"} + +function _evalcache () { + local cmdHash="nohash" data="$*" name + + # use the first non-variable argument as the name + for name in $@; do + if [ "${name}" = "${name#[A-Za-z_][A-Za-z0-9_]*=}" ]; then + break + fi + done + + # if command is a function, include its definition in data + if typeset -f "${name}" > /dev/null; then + data=${data}$(typeset -f "${name}") + fi + + if builtin command -v md5 > /dev/null; then + cmdHash=$(echo -n "${data}" | md5) + elif builtin command -v md5sum > /dev/null; then + cmdHash=$(echo -n "${data}" | md5sum | cut -d' ' -f1) + fi + + local cacheFile="$ZSH_EVALCACHE_DIR/init-${name##*/}-${cmdHash}.sh" + + if [ "$ZSH_EVALCACHE_DISABLE" = "true" ]; then + eval ${(q)@} + elif [ -s "$cacheFile" ]; then + source "$cacheFile" + else + if type "${name}" > /dev/null; then + echo "evalcache: ${name} initialization not cached, caching output of: $*" >&2 + mkdir -p "$ZSH_EVALCACHE_DIR" + eval ${(q)@} > "$cacheFile" + source "$cacheFile" + else + echo "evalcache: ERROR: ${name} is not installed or in PATH" >&2 + fi + fi +} + +function _evalcache_clear () { + rm -i "$ZSH_EVALCACHE_DIR"/init-*.sh +}