├── .editorconfig ├── .gitignore ├── .zshrc ├── LICENSE ├── README.md ├── antidote.lite.zsh ├── examples ├── antidote_lite_example.zsh ├── full_featured.zsh ├── ohmyzsh_zsh_custom_zshrc.zsh ├── ohmyzsh_zshrc.zsh ├── prezto_zshrc.zsh ├── zshrc.zsh └── zshrc_clone.zsh ├── tests ├── __init__.zsh ├── run-clitests ├── test-advanced-zshrc.md ├── test-unplugged.md └── test-zsh-unplugged.md ├── unplugged.zsh └── zsh_unplugged.zsh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.zsh] 12 | indent_size = 2 13 | 14 | [*.{md,markdown}] 15 | # Don't trim end spacing because unfortunately Markdown uses that 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/.ztap/ 2 | tests/.plugins/ 3 | *.zwc 4 | *.old 5 | -------------------------------------------------------------------------------- /.zshrc: -------------------------------------------------------------------------------- 1 | # .zshrc 2 | 3 | # Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc. 4 | # Initialization code that may require console input (password prompts, [y/n] 5 | # confirmations, etc.) must go above this block; everything else may go below. 6 | if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then 7 | source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" 8 | fi 9 | 10 | # Set Zsh options. 11 | set extended_glob 12 | 13 | # Setup vars similar to Oh My Zsh. 14 | ZSH=${ZSH:-${ZDOTDIR:-$HOME/.config/zsh}} 15 | ZSH_CUSTOM=${ZSH_CUSTOM:-$ZSH/custom} 16 | 17 | # Use antidote.lite - a Zsh micro plugin manager based on zsh_unplugged. 18 | if [[ ! -e $ZSH/lib/antidote.lite.zsh ]]; then 19 | mkdir -p $ZSH/lib 20 | curl -fsSL -o $ZSH/lib/antidote.lite.zsh \ 21 | https://raw.githubusercontent.com/mattmc3/zsh_unplugged/main/antidote.lite.zsh 22 | fi 23 | 24 | # load any files in your lib directory 25 | for zlib in $ZSH/lib/*.zsh(N); source $zlib 26 | unset zlib 27 | 28 | # util (path) plugins 29 | myutils=( 30 | romkatv/zsh-bench 31 | ) 32 | 33 | # promt (fpath) plugins 34 | myprompts=( 35 | sindresorhus/pure 36 | romkatv/powerlevel10k 37 | ) 38 | 39 | # regular Zsh plugins 40 | myplugins=( 41 | # put anything you want in $ZSH_CUSTOM/plugins just like OMZ 42 | # git 43 | # python 44 | # etc 45 | 46 | # core plugins 47 | mattmc3/zephyr/plugins/color 48 | mattmc3/zephyr/plugins/directory 49 | mattmc3/zephyr/plugins/editor 50 | mattmc3/zephyr/plugins/environment 51 | mattmc3/zephyr/plugins/history 52 | mattmc3/zephyr/plugins/utility 53 | mattmc3/zephyr/plugins/prompt 54 | 55 | # oh-my-zsh plugins 56 | ohmyzsh/ohmyzsh/lib/clipboard.zsh 57 | ohmyzsh/ohmyzsh/plugins/colored-man-pages 58 | ohmyzsh/ohmyzsh/plugins/magic-enter 59 | 60 | # completions 61 | zsh-users/zsh-completions 62 | mattmc3/zephyr/plugins/completion 63 | 64 | # fish-like plugins 65 | zsh-users/zsh-syntax-highlighting 66 | zsh-users/zsh-autosuggestions 67 | zsh-users/zsh-history-substring-search 68 | ) 69 | 70 | # clone and load 71 | plugin-clone $myplugins $myprompts $myutils 72 | plugin-load --kind path $myutils 73 | plugin-load --kind fpath $myprompts 74 | plugin-load $myplugins 75 | 76 | # pick your prompt (options: pure, powerlevel10k, starship) 77 | prompt powerlevel10k 78 | 79 | # To customize prompt, run `p10k configure` or edit ~/.zsh/.p10k.zsh. 80 | [[ ! -f ${ZDOTDIR:-$HOME}/.p10k.zsh ]] || source ${ZDOTDIR:-$HOME}/.p10k.zsh 81 | typeset -g POWERLEVEL9K_INSTANT_PROMPT=quiet 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zsh_unplugged 2 | 3 | > 🤔 perhaps you don't need a Zsh plugin manager after all... 4 | 5 | TLDR; You don't need a big bloated plugin manager for your Zsh plugins. A simple 6 | ~20 line function may be all you need. 7 | 8 | Click here to [skip to the code](#jigsaw-the-humble-plugin-load-function). 9 | 10 | ## :electric_plug: Zsh Plugin Managers 11 | 12 | ### :newspaper_roll: Current state 13 | 14 | There are an embarrassingly large number of Zsh plugin managers out there. Many of them 15 | are abandonware, are no longer actively developed, are brand new without many users, or 16 | don't have much reason to even exist other than as a novelty. 17 | 18 | Here's a list of many (but certainly not all) of them from [awesome-zsh-plugins]: 19 | 20 | | Zsh Plugin Manager | Performance | Current state | 21 | |--------------------|--------------------|-------------------------------------------------| 22 | | [antibody] | :rabbit2: fast | :imp: Maintenance mode, no new features | 23 | | [antigen] | :turtle: slow | :imp: Maintenance mode, no new features | 24 | | [antidote] | :rabbit2: fast | :white_check_mark: Active | 25 | | [sheldon] | :question: unknown | :white_check_mark: Active | 26 | | [zcomet] | :rabbit2: fast | :white_check_mark: Active | 27 | | [zgem] | :question: unknown | :skull_and_crossbones: Abandonware | 28 | | [zgen] | :rabbit2: fast | :skull_and_crossbones: Abandonware | 29 | | [zgenom] | :rabbit2: fast | :white_check_mark: Active | 30 | | [zinit-continuum] | :rabbit2: fast | :white_check_mark: Active [\*][#1] | 31 | | [zinit] | :rabbit2: fast | :cursing_face: Author deleted project | 32 | | [zit] | :question: unknown | :imp: Few/no recent commits | 33 | | [znap] | :rabbit2: fast | :white_check_mark: Active | 34 | | [zplug] | :turtle: slow | :skull_and_crossbones: Abandonware | 35 | | [zplugin][zinit] | :rabbit2: fast | :cursing_face: Renamed to zinit, author deleted | 36 | | [zpm] | :rabbit2: fast | :white_check_mark: Active | 37 | | [zr] | :rabbit2: fast | :imp: Complete, bugfixes welcome | 38 | 39 | _Full disclosure, I'm the author of one of these - [antidote] (formerly called [pz])._ 40 | 41 | There's new ones popping up all the time too: 42 | 43 | | Zsh Plugin Manager | Performance | Current state | 44 | |--------------------|--------------------|----------------------| 45 | | [mzpm] | :question: unknown | :hatching_chick: New | 46 | | [tzpm] | :question: unknown | :hatching_chick: New | 47 | | [uz] | :question: unknown | :hatching_chick: New | 48 | | [zed] | :question: unknown | :hatching_chick: New | 49 | 50 | ### :firecracker: The catalyst 51 | 52 | In January 2021, the plugin manager I was using, [antibody], was deprecated. 53 | The author even [went so far as to say](https://github.com/getantibody/antibody/tree/2ca7616ae78754c0ab70790229f5d19be42206e9): 54 | 55 | > Most of the other plugin managers catch up on performance, thus keeping this \[antibody] does not make sense anymore. 56 | 57 | Prior to that, I used [zgen], which also stopped being actively developed and the 58 | [developer](https://github.com/tarjoilija) seems to have disappeared. (_Shoutout 59 | to @jandamm for carrying on Zgen with [Zgenom](https://github.com/jandamm/zgenom)!_) 60 | 61 | In November 2021, a relatively well known and popular Zsh plugin manager, zinit, was 62 | removed from GitHub entirely and without warning. In fact, the author 63 | [deleted almost his entire body of work][zdharma-debacle]. Zinit was really popular 64 | because it was super fast, and the author promoted his projects in multiple venues 65 | for many years. (_Shoutout to [zdharma-continuum] for carrying on with zinit!_) 66 | 67 | With all the instability in the Zsh plugin manager space, it got me wondering why I 68 | even bother with a plugin manager at all. 69 | 70 | ### :bulb: The simple idea 71 | 72 | After [antibody] was deprecated, I tried [znap], but it was in early development at the 73 | time and kept breaking, so like many others before me, I decided to write my own - [antidote]. 74 | 75 | When developing [antidote], my goal was simple - make a plugin manager 76 | that was fast, functional, and easy to understand - which was everything I loved about 77 | [zgen] and [antibody]. While [antidote] is a great project, and I fully recommend it 78 | if you want to use a plugin manager, I kept wondering if I could cut further 79 | down to a single _function_ and see what it would take to not use plugin management 80 | utilities altogether. 81 | 82 | Thus was born... **zsh_unplugged**. 83 | 84 | This isn't a plugin manager - it's a way to show you how to manage your own plugins 85 | using small, easy to understand snippets of Zsh. All this with the thought that perhaps, 86 | once-and-for-all, we can demystify what plugin managers do. And for basic configs do 87 | away with using a plugin manager altogether and simply do it ourselves. 88 | 89 | You can grab a ~20 line function and you have everything you need to manage your own 90 | plugins from here on out. By way of contrast, I ran [scc](https://github.com/boyter/scc) 91 | against the zinit project which comes out to thousands of lines of Zsh code, along with 92 | thousands of lines in supporting project files.\*! 93 | 94 | ```zsh 95 | $ zinit_tmpdir=$(mktemp -d) 96 | $ git clone --depth 1 https://github.com/zdharma-continuum/zinit $zinit_tmpdir 97 | $ git -C $zinit_tmpdir rev-parse --short HEAD 98 | 1375adf8 99 | $ scc --no-cocomo $zinit_tmpdir 100 | $ test -d $zinit_tmpdir && rm -rf -- $zinit_tmpdir 101 | ``` 102 | 103 | Results: 104 | ```text 105 | ─────────────────────────────────────────────────────────────────────────────── 106 | Language Files Lines Blanks Comments Code Complexity 107 | ─────────────────────────────────────────────────────────────────────────────── 108 | YAML 16 643 84 78 481 0 109 | Zsh 14 10971 937 1352 8682 1782 110 | AsciiDoc 6 5375 1746 0 3629 0 111 | Markdown 6 2251 603 0 1648 0 112 | Shell 3 674 84 56 534 60 113 | Makefile 2 111 27 12 72 7 114 | SVG 2 683 2 2 679 0 115 | Docker ignore 1 10 1 0 9 0 116 | Dockerfile 1 41 9 2 30 8 117 | JSON 1 47 0 0 47 0 118 | License 1 22 4 0 18 0 119 | gitignore 1 24 0 0 24 0 120 | ─────────────────────────────────────────────────────────────────────────────── 121 | Total 54 20852 3497 1502 15853 1857 122 | ─────────────────────────────────────────────────────────────────────────────── 123 | Processed 816700 bytes, 0.817 megabytes (SI) 124 | ─────────────────────────────────────────────────────────────────────────────── 125 | ``` 126 | 127 | \**Note: SLOC is not intended as anything more here than a rough comparison of effort, maintainability, and complexity* 128 | 129 | ## :tada: The code 130 | 131 | ### :gear: The bare metal way 132 | 133 | If you don't want to use anything resembling a plugin manager at all, you could simply 134 | clone and source plugins yourself manually: 135 | 136 | ```zsh 137 | ZPLUGINDIR=$HOME/.zsh/plugins 138 | 139 | if [[ ! -d $ZPLUGINDIR/zsh-autosuggestions ]]; then 140 | git clone https://github.com/zsh-users/zsh-autosuggestions \ 141 | $ZPLUGINDIR/zsh-autosuggestions 142 | fi 143 | source $ZPLUGINDIR/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh 144 | 145 | if [[ ! -d $ZPLUGINDIR/zsh-history-substring-search ]]; then 146 | git clone https://github.com/zsh-users/zsh-history-substring-search \ 147 | $ZPLUGINDIR/zsh-history-substring-search 148 | fi 149 | source $ZPLUGINDIR/zsh-history-substring-search/zsh-history-substring-search.plugin.zsh 150 | 151 | if [[ ! -d $ZPLUGINDIR/z ]]; then 152 | git clone https://github.com/rupa/z \ 153 | $ZPLUGINDIR/z 154 | fi 155 | source $ZPLUGINDIR/z/z.sh 156 | ``` 157 | 158 | This can get pretty repetitive, cumbersome, and tricky to maintain. You need to figure out 159 | each plugin's init file, and sometimes adding a plugin to your `fpath` is required. While 160 | this method works, there's another way... 161 | 162 | ### :jigsaw: The humble `plugin-load` function 163 | 164 | If we go one level of abstraction higher than manually calling `git clone`, we can 165 | use a simple function as the basis for everything you need to manage Zsh plugins: 166 | 167 | ```zsh 168 | ##? Clone a plugin, identify its init file, source it, and add it to your fpath. 169 | function plugin-load { 170 | local repo plugdir initfile initfiles=() 171 | : ${ZPLUGINDIR:=${ZDOTDIR:-~/.config/zsh}/plugins} 172 | for repo in $@; do 173 | plugdir=$ZPLUGINDIR/${repo:t} 174 | initfile=$plugdir/${repo:t}.plugin.zsh 175 | if [[ ! -d $plugdir ]]; then 176 | echo "Cloning $repo..." 177 | git clone -q --depth 1 --recursive --shallow-submodules \ 178 | https://github.com/$repo $plugdir 179 | fi 180 | if [[ ! -e $initfile ]]; then 181 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 182 | (( $#initfiles )) || { echo >&2 "No init file '$repo'." && continue } 183 | ln -sf $initfiles[1] $initfile 184 | fi 185 | fpath+=$plugdir 186 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 187 | done 188 | } 189 | ``` 190 | 191 | That's it. ~20 lines of code and you have a simple, robust Zsh plugin management 192 | alternative that is likely as fast as most everything else out there. 193 | 194 | What this does is simply clones a Zsh plugin's git repository, and then examines that 195 | repo for an appropriate .zsh file to use as an init script. We then find and symlink 196 | the plugin's init file if necessary, which allows us to get close to the performance 197 | advantage of static sourcing rather than searching for which plugin file to load every 198 | time we open a new terminal. 199 | 200 | Then, the plugin is sourced and added to `fpath`. 201 | 202 | You can even get turbocharged-hypersonic-load-speed-magic :rocket: if you really need 203 | every last bit of performance. 204 | [See how here](#question-how-do-i-load-my-plugins-with-hypersonic-speed-rocket). 205 | 206 | ### :question: How do you use this in your own Zsh config? 207 | 208 | You are free to grab the `plugin-load` function above and put it directly in your 209 | .zshrc, maintain it yourself, and never rely on anyone else's plugin manager again. Or, 210 | this repo makes the plugin-load function available as a plugin itself if you prefer. 211 | Here's an example .zshrc: 212 | 213 | ```zsh 214 | # where do you want to store your plugins? 215 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 216 | 217 | # get zsh_unplugged and store it with your other plugins 218 | if [[ ! -d $ZPLUGINDIR/zsh_unplugged ]]; then 219 | git clone --quiet https://github.com/mattmc3/zsh_unplugged $ZPLUGINDIR/zsh_unplugged 220 | fi 221 | source $ZPLUGINDIR/zsh_unplugged/zsh_unplugged.zsh 222 | 223 | # make list of the Zsh plugins you use 224 | repos=( 225 | # plugins that you want loaded first 226 | sindresorhus/pure 227 | 228 | # other plugins 229 | zsh-users/zsh-completions 230 | rupa/z 231 | # ... 232 | 233 | # plugins you want loaded last 234 | zsh-users/zsh-syntax-highlighting 235 | zsh-users/zsh-history-substring-search 236 | zsh-users/zsh-autosuggestions 237 | ) 238 | 239 | # now load your plugins 240 | plugin-load $repos 241 | ``` 242 | 243 | Here is an sample [.zshrc](https://github.com/mattmc3/zsh_unplugged/blob/main/examples/zshrc.zsh). 244 | 245 | ### :question: Could I use this to make a micro-zsh-plugin-manager? 246 | 247 | Yes! This project uses the [unlicense](https://unlicense.org/). Feel free to use this 248 | code anywhere. Or, if you prefer to use something already built and supported, this 249 | project includes its own implemetation of a micro plugin manager in the 250 | [antidote.lite.zsh](antidote.lite.zsh) file. It's ~100 lines of code. 251 | 252 | You can view a full featured example of using zsh_unplugged in the 253 | [full_featured.zsh example file](examples/full_featured.zsh). 254 | 255 | ### :question: How do I update my plugins? 256 | 257 | Updating your plugins is as simple as deleting the $ZPLUGINDIR and reloading Zsh. 258 | 259 | ```zsh 260 | ZPLUGINDIR=~/.config/zsh/plugins 261 | rm -rfi $ZPLUGINDIR 262 | zsh 263 | ``` 264 | 265 | If you are comfortable with `git` commands and prefer to not rebuild everything, you 266 | can run `git pull` yourself, or even use a simple `plugin-update` function: 267 | 268 | ```zsh 269 | function plugin-update { 270 | ZPLUGINDIR=${ZPLUGINDIR:-$HOME/.config/zsh/plugins} 271 | for d in $ZPLUGINDIR/*/.git(/); do 272 | echo "Updating ${d:h:t}..." 273 | command git -C "${d:h}" pull --ff --recurse-submodules --depth 1 --rebase --autostash 274 | done 275 | } 276 | ``` 277 | 278 | ### :question: How do I list my plugins? 279 | 280 | You can see what plugins you have installed with a simple `ls` command: 281 | 282 | ```zsh 283 | ls $ZPLUGINDIR 284 | ``` 285 | 286 | If you need something fancier and would like to see the git origin of your plugins, you 287 | could run this command: 288 | 289 | ```zsh 290 | for d in $ZPLUGINDIR/*/.git; do 291 | git -C "${d:h}" remote get-url origin 292 | done 293 | ``` 294 | 295 | ### :question: How do I remove a plugin? 296 | 297 | You can just remove it from your `plugins` list in your .zshrc. To delete it 298 | altogether, feel free to run `rm`: 299 | 300 | ```zsh 301 | # remove the fast-syntax-highlighting plugin 302 | rm -rfi $ZPLUGINDIR/fast-syntax-highlighting 303 | ``` 304 | 305 | ### :question: How do I load my plugins with hypersonic speed :rocket:? 306 | 307 | You can get turbocharged-hypersonic-load-speed-magic if you choose to use the 308 | [romkatv/zsh-defer](https://github.com/romkatv/zsh-defer) plugin. Essentially, if you 309 | add `romkatv/zsh-defer` to your plugins list, everything you load afterwards will use 310 | zsh-defer, meaning you'll get speeds similar to zinit's [turbo mode](https://github.com/zdharma-continuum/zinit#turbo-and-lucid). 311 | 312 | Notably, if you like the [zsh-abbr] plugin for fish-like abbreviations in Zsh, 313 | using zsh-defer [will boost performance greatly](https://github.com/olets/zsh-abbr/issues/52). 314 | 315 | :warning: Warning - the author of zsh-defer does not recommend using the plugin this 316 | way, so be careful and selective about which plugins you load with zsh-defer. If you 317 | get weird behavior from a plugin, then load it before zsh-defer. In my extensive 318 | testing, the biggest benefit came only from especially sluggish plugins like [zsh-abbr]. 319 | 320 | ### :question: What if I need to customize how a plugin is loaded? 321 | 322 | You can separate the clone and load actions into two separate functions, allowing you 323 | to further customize how you handle plugins. This technique is especially useful if you 324 | are using a project like [zsh-utils] with nested plugins, or using utilities like 325 | [zsh-bench] which aren't plugins. 326 | 327 | ```zsh 328 | # declare a simple plugin-clone function, leaving the user to source plugins themselves 329 | function plugin-clone { 330 | local repo plugdir initfile initfiles=() 331 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 332 | for repo in $@; do 333 | plugdir=$ZPLUGINDIR/${repo:t} 334 | initfile=$plugdir/${repo:t}.plugin.zsh 335 | if [[ ! -d $plugdir ]]; then 336 | echo "Cloning $repo..." 337 | git clone -q --depth 1 --recursive --shallow-submodules \ 338 | https://github.com/$repo $plugdir 339 | fi 340 | if [[ ! -e $initfile ]]; then 341 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 342 | (( $#initfiles )) && ln -sf $initfiles[1] $initfile 343 | fi 344 | done 345 | } 346 | 347 | # now, plugin-source is a separate thing 348 | function plugin-source { 349 | local plugdir 350 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 351 | for plugdir in $@; do 352 | [[ $plugdir = /* ]] || plugdir=$ZPLUGINDIR/$plugdir 353 | fpath+=$plugdir 354 | local initfile=$plugdir/${plugdir:t}.plugin.zsh 355 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 356 | done 357 | } 358 | ``` 359 | 360 | You can then use these two functions like so: 361 | 362 | ```zsh 363 | # make a list of github repos 364 | repos=( 365 | # not-sourcable plugins 366 | romkatv/zsh-bench 367 | 368 | # projects with nested plugins 369 | belak/zsh-utils 370 | ohmyzsh/ohmyzsh 371 | 372 | # regular plugins 373 | zsh-users/zsh-autosuggestions 374 | zsh-users/zsh-history-substring-search 375 | zdharma-continuum/fast-syntax-highlighting 376 | ) 377 | plugin-clone $repos 378 | 379 | # zsh-bench doesn't have a plugin file 380 | # it just needs added to your $PATH 381 | export PATH="$ZPLUGINDIR/zsh-bench:$PATH" 382 | 383 | # Oh-My-Zsh plugins rely on stuff in its lib directory 384 | ZSH=$ZPLUGINDIR/ohmyzsh 385 | for _f in $ZSH/lib/*.zsh; do 386 | source $_f 387 | done 388 | unset _f 389 | 390 | # source other plugins 391 | plugins=( 392 | zsh-utils/history 393 | zsh-utils/complete 394 | zsh-utils/utility 395 | ohmyzsh/plugins/magic-enter 396 | ohmyzsh/plugins/history-substring-search 397 | ohmyzsh/plugins/z 398 | fast-syntax-highlighting 399 | zsh-autosuggestions 400 | ) 401 | plugin-source $plugins 402 | ``` 403 | 404 | Here is a sample [.zshrc](https://github.com/mattmc3/zsh_unplugged/blob/main/examples/zshrc_clone.zsh). 405 | 406 | ### :question: What if I want my plugins to be even faster? 407 | 408 | If you are an experienced Zsh user, you may know about [zcompile], which takes your 409 | Zsh scripts and potentially speeds them up by compiling them to byte code. If you feel 410 | confident you know what you're doing and want to eek every last bit of performance out 411 | of your Zsh, you can use this function: 412 | 413 | ```zsh 414 | function plugin-compile { 415 | ZPLUGINDIR=${ZPLUGINDIR:-$HOME/.config/zsh/plugins} 416 | autoload -U zrecompile 417 | local f 418 | for f in $ZPLUGINDIR/**/*.zsh{,-theme}(N); do 419 | zrecompile -pq "$f" 420 | done 421 | } 422 | ``` 423 | 424 | ### :question: How can I use this with Zsh frameworks like Oh-My-Zsh or Prezto? 425 | 426 | [Oh-My-Zsh][ohmyzsh] and [Prezto][prezto] have their own built-in methods for loading 427 | plugins, they just don't come with a way to clone them. You don't need the zsh_unplugged 428 | script if you are using those frameworks. However, you also don't need a separate plugin 429 | manager utility. Here's how you handle cloning yourself and go plugin-manager-free with 430 | Zsh frameworks: 431 | 432 | #### Oh-My-Zsh 433 | 434 | If you are using [Oh-My-Zsh][ohmyzsh], the way to go without a plugin manager would be 435 | to utilize the `$ZSH_CUSTOM` path. 436 | 437 | _Note that this assumes your init file is called {plugin_name}.plugin.zsh which may not 438 | be true._ 439 | 440 | ```zsh 441 | # .zshrc 442 | # don't call this list 'plugins' since omz uses that 443 | repos=( 444 | marlonrichert/zsh-hist 445 | zsh-users/zsh-syntax-highlighting 446 | zsh-users/zsh-autosuggestions 447 | ) 448 | for repo in $repos; do 449 | if [[ ! -d $ZSH_CUSTOM/${repo:t} ]]; then 450 | git clone https://github.com/${repo} $ZSH_CUSTOM/plugins/${repo:t} 451 | fi 452 | done 453 | unset repo{s,} 454 | 455 | # add your external plugins to your OMZ plugins list 456 | plugins=( 457 | ... 458 | zsh-hist 459 | zsh-autosuggestions 460 | ... 461 | zsh-syntax-highlighting 462 | ) 463 | ``` 464 | 465 | #### Prezto 466 | 467 | If you are using [Prezto][prezto], the way to go without a plugin manager would be to 468 | utilize the `$ZPREZTODIR/contrib` path. 469 | 470 | _Note that this assumes your init file is called {plugin_name}.plugin.zsh which may not 471 | be true._ 472 | 473 | ```zsh 474 | # .zshrc 475 | contribs=( 476 | rupa/z 477 | marlonrichert/zsh-hist 478 | mattmc3/zman 479 | ) 480 | for contrib in $contribs; do 481 | if [[ ! -d $ZPREZTODIR/contrib/${contrib:t} ]]; then 482 | git clone https://github.com/${contrib} $ZPREZTODIR/contrib/${contrib:t} 483 | fi 484 | done 485 | unset contrib{,s} 486 | 487 | # add the contribs to your Prezto modules list in your `.zpreztorc` 488 | zstyle ':prezto:load' pmodule \ 489 | ... \ 490 | z \ 491 | zsh-hist \ 492 | ... \ 493 | zman 494 | ``` 495 | 496 | [zinit-docs-reddit]: https://www.reddit.com/r/zsh/comments/mur6eu/anyone_interested_in_zinit_documentation/ 497 | [awesome-zsh-plugins]: https://github.com/unixorn/awesome-zsh-plugins 498 | [zdharma-debacle]: https://www.reddit.com/r/zsh/comments/qinb6j/httpsgithubcomzdharma_has_suddenly_disappeared_i/ 499 | [zdharma-continuum]: https://github.com/zdharma-continuum 500 | [zcompile]: https://github.com/antonio/zsh-config/blob/master/help/zcompile 501 | [antibody]: https://github.com/getantibody/antibody 502 | [antigen]: https://github.com/zsh-users/antigen 503 | [mzpm]: https://github.com/xylous/mzpm 504 | [antidote]: https://github.com/mattmc3/antidote 505 | [pz]: https://github.com/mattmc3/antidote/tree/pz 506 | [sheldon]: https://github.com/rossmacarthur/sheldon 507 | [tzpm]: https://github.com/notusknot/tzpm 508 | [uz]: https://github.com/maxrodrigo/uz 509 | [zcomet]: https://github.com/agkozak/zcomet 510 | [zed]: https://github.com/MunifTanjim/zed 511 | [zgem]: https://github.com/qoomon/zgem 512 | [zgen]: https://github.com/tarjoilija/zgen 513 | [zgenom]: https://github.com/jandamm/zgenom 514 | [zinit-continuum]: https://github.com/zdharma-continuum/zinit 515 | [zinit]: https://github.com/zdharma/zinit 516 | [zit]: https://github.com/thiagokokada/zit 517 | [znap]: https://github.com/marlonrichert/zsh-snap 518 | [zplug]: https://github.com/zplug/zplug 519 | [zpm]: https://github.com/zpm-zsh/zpm 520 | [zr]: https://github.com/jedahan/zr 521 | [#1]: https://github.com/mattmc3/zsh_unplugged/issues/1 522 | [ohmyzsh]: https://github.com/ohmyzsh/ohmyzsh 523 | [prezto]: https://github.com/sorin-ionescu/prezto 524 | [pure]: https://github.com/sindresorhus/pure 525 | [zsh-abbr]: https://github.com/olets/zsh-abbr 526 | [zsh-utils]: https://github.com/belak/zsh-utils 527 | [zsh-bench]: https://github.com/romkatv/zsh-bench 528 | -------------------------------------------------------------------------------- /antidote.lite.zsh: -------------------------------------------------------------------------------- 1 | # antidote.lite - a micro zsh plugin manager based on antidote and zsh_unplugged. 2 | # author: mattmc3 3 | # home: https://github.com/mattmc3/zsh_unplugged 4 | # https://github.com/mattmc3/antidote 5 | # license: https://unlicense.org 6 | # usage: plugin-load $myplugins 7 | # version: 0.0.4 8 | 9 | # Set variables. 10 | : ${ANTIDOTE_LITE_HOME:=${XDG_CACHE_HOME:-~/.cache}/antidote.lite} 11 | : ${ZPLUGINDIR:=${ZSH_CUSTOM:-${ZDOTDIR:-${XDG_CONFIG_HOME:-$HOME/.config}/zsh}}/plugins} 12 | typeset -gHa _alite_zopts=(extended_glob glob_dots no_monitor) 13 | 14 | ##? Clone zsh plugins in parallel. 15 | function plugin-clone { 16 | emulate -L zsh; setopt local_options $_alite_zopts 17 | local repo plugdir; local -Ua repos 18 | 19 | # Remove bare words ${(M)@:#*/*} and paths with leading slash ${@:#/*}. 20 | # Then split/join to keep the 2-part user/repo form to bulk-clone repos. 21 | for repo in ${${(M)@:#*/*}:#/*}; do 22 | repo=${(@j:/:)${(@s:/:)repo}[1,2]} 23 | [[ -e $ANTIDOTE_LITE_HOME/$repo ]] || repos+=$repo 24 | done 25 | 26 | for repo in $repos; do 27 | plugdir=$ANTIDOTE_LITE_HOME/$repo 28 | if [[ ! -d $plugdir ]]; then 29 | echo "Cloning $repo..." 30 | ( 31 | command git clone -q --depth 1 --recursive --shallow-submodules \ 32 | ${ANTIDOTE_LITE_GITURL:-https://github.com/}$repo $plugdir 33 | plugin-compile $plugdir 34 | ) & 35 | fi 36 | done 37 | wait 38 | } 39 | 40 | ##? Load zsh plugins. 41 | function plugin-load { 42 | source <(plugin-script $@) 43 | } 44 | 45 | ##? Script loading of zsh plugins. 46 | function plugin-script { 47 | emulate -L zsh; setopt local_options $_alite_zopts 48 | 49 | # parse args 50 | local kind # kind=path,fpath 51 | while (( $# )); do 52 | case $1 in 53 | -k|--kind) shift; kind=$1 ;; 54 | -*) echo >&2 "Invalid argument '$1'." && return 2 ;; 55 | *) break ;; 56 | esac 57 | shift 58 | done 59 | 60 | local plugin src="source" inits=() 61 | (( ! $+functions[zsh-defer] )) || src="zsh-defer ." 62 | for plugin in $@; do 63 | if [[ -n "$kind" ]]; then 64 | echo "$kind=(\$$kind $ANTIDOTE_LITE_HOME/$plugin)" 65 | else 66 | inits=( 67 | {$ZPLUGINDIR,$ANTIDOTE_LITE_HOME}/$plugin/${plugin:t}.{plugin.zsh,zsh-theme,zsh,sh}(N) 68 | $ANTIDOTE_LITE_HOME/$plugin/*.{plugin.zsh,zsh-theme,zsh,sh}(N) 69 | $ANTIDOTE_LITE_HOME/$plugin(N) 70 | ${plugin}/*.{plugin.zsh,zsh-theme,zsh,sh}(N) 71 | ${plugin}(N) 72 | ) 73 | (( $#inits )) || { echo >&2 "No plugin init found '$plugin'." && continue } 74 | plugin=$inits[1] 75 | echo "fpath=(\$fpath $plugin:h)" 76 | echo "$src $plugin" 77 | [[ "$plugin:h:t" == zsh-defer ]] && src="zsh-defer ." 78 | fi 79 | done 80 | } 81 | 82 | ##? Update plugins. 83 | function plugin-update { 84 | emulate -L zsh; setopt local_options $_alite_zopts 85 | local plugdir oldsha newsha 86 | for plugdir in $ANTIDOTE_LITE_HOME/*/*/.git(N/); do 87 | plugdir=${plugdir:A:h} 88 | echo "Updating ${plugdir:h:t}/${plugdir:t}..." 89 | ( 90 | oldsha=$(command git -C $plugdir rev-parse --short HEAD) 91 | command git -C $plugdir pull --quiet --ff --depth 1 --rebase --autostash 92 | newsha=$(command git -C $plugdir rev-parse --short HEAD) 93 | [[ $oldsha == $newsha ]] || echo "Plugin updated: $plugdir:t ($oldsha -> $newsha)" 94 | ) & 95 | done 96 | wait 97 | plugin-compile 98 | echo "Update complete." 99 | } 100 | 101 | ##? Compile plugins. 102 | function plugin-compile { 103 | emulate -L zsh; setopt local_options $_alite_zopts 104 | autoload -Uz zrecompile 105 | local zfile 106 | for zfile in ${1:-$ANTIDOTE_LITE_HOME}/**/*.zsh{,-theme}(N); do 107 | [[ $zfile != */test-data/* ]] || continue 108 | zrecompile -pq "$zfile" 109 | done 110 | } 111 | -------------------------------------------------------------------------------- /examples/antidote_lite_example.zsh: -------------------------------------------------------------------------------- 1 | # 2 | # .zshrc - Run on interactive Zsh session. 3 | # 4 | 5 | # vars 6 | myrepos=( 7 | mbadolato/iTerm2-Color-Schemes 8 | ) 9 | 10 | myutils=( 11 | romkatv/zsh-bench 12 | ) 13 | 14 | myprompts=( 15 | sindresorhus/pure 16 | romkatv/powerlevel10k 17 | ) 18 | 19 | myplugins=( 20 | mattmc3/zfunctions 21 | mattmc3/zman 22 | rupa/z 23 | ohmyzsh/ohmyzsh/lib/clipboard.zsh 24 | ohmyzsh/ohmyzsh/plugins/copybuffer 25 | ohmyzsh/ohmyzsh/plugins/magic-enter 26 | ohmyzsh/ohmyzsh/plugins/fancy-ctrl-z 27 | belak/zsh-utils/editor 28 | belak/zsh-utils/history 29 | belak/zsh-utils/prompt 30 | belak/zsh-utils/utility 31 | belak/zsh-utils/completion 32 | 33 | # deferred 34 | romkatv/zsh-defer 35 | olets/zsh-abbr 36 | zdharma-continuum/fast-syntax-highlighting 37 | zsh-users/zsh-autosuggestions 38 | zsh-users/zsh-history-substring-search 39 | ) 40 | 41 | # antidote.lite 42 | source $ZDOTDIR/lib/antidote.lite.zsh 43 | plugin-clone $myrepos $myutils $myprompts $myplugins 44 | plugin-load --kind path $myutils 45 | plugin-load --kind fpath $myprompts 46 | plugin-load $myplugins 47 | 48 | # prompt 49 | prompt pure 50 | 51 | # vim: ft=zsh sw=2 ts=2 et 52 | -------------------------------------------------------------------------------- /examples/full_featured.zsh: -------------------------------------------------------------------------------- 1 | # .zshrc 2 | 3 | # Clone zsh_unplugged and use it as a micro plugin manager. 4 | [[ -d $ZDOTDIR/.unplugged ]] || 5 | git clone https://github.com/mattmc3/zsh_unplugged $ZDOTDIR/.unplugged 6 | source $ZDOTDIR/.unplugged/zsh_unplugged.zsh 7 | 8 | # clone-only plugins 9 | plugin-clone romkatv/zsh-bench 10 | path+=$ZPLUGINDIR/romkatv/zsh-bench 11 | 12 | # load plugins 13 | plugins=( 14 | # Uncomment to load your custom zstyles. 15 | # $ZDOTDIR/.zpreztorc 16 | # $ZDOTDIR/.zstyles 17 | 18 | # regular plugins 19 | mattmc3/zman 20 | zshzoo/macos 21 | agkozak/zsh-z 22 | 23 | # oh-my-zsh plugins 24 | ohmyzsh/ohmyzsh/lib/clipboard.zsh 25 | ohmyzsh/ohmyzsh/plugins/copyfile 26 | ohmyzsh/ohmyzsh/plugins/copypath 27 | ohmyzsh/ohmyzsh/plugins/copybuffer 28 | ohmyzsh/ohmyzsh/plugins/magic-enter 29 | ohmyzsh/ohmyzsh/plugins/fancy-ctrl-z 30 | ohmyzsh/ohmyzsh/plugins/extract 31 | 32 | # prezto modules 33 | sorin-ionescu/prezto/runcoms/zprofile 34 | sorin-ionescu/prezto/modules/terminal 35 | sorin-ionescu/prezto/modules/editor 36 | sorin-ionescu/prezto/modules/history 37 | sorin-ionescu/prezto/modules/directory 38 | 39 | # Uncomment to use your local plugins 40 | # Put these in $ZDOTDIR/plugins 41 | # my_plugin 42 | # python 43 | 44 | # prompt 45 | sindresorhus/pure 46 | 47 | # do completions 48 | zsh-users/zsh-completions 49 | sorin-ionescu/prezto/modules/completion 50 | 51 | # Deferred plugins may speed up your load times even more. 52 | # Once you load romkatv/zsh-defer, everything after gets deferred. 53 | romkatv/zsh-defer 54 | olets/zsh-abbr 55 | zdharma-continuum/fast-syntax-highlighting 56 | zsh-users/zsh-autosuggestions 57 | zsh-users/zsh-history-substring-search 58 | ) 59 | plugin-load $plugins 60 | -------------------------------------------------------------------------------- /examples/ohmyzsh_zsh_custom_zshrc.zsh: -------------------------------------------------------------------------------- 1 | # ...standard oh-my-zsh boilerplate goes here... 2 | ZSH=${ZSH:-$HOME/.oh-my-zsh} 3 | ZSH_CUSTOM=${ZSH_CUSTOM:-$ZSH/custom} 4 | 5 | # clone your external plugins if needed 6 | external_plugins=( 7 | zsh-users/zsh-autosuggestions 8 | marlonrichert/zsh-hist 9 | zsh-users/zsh-syntax-highlighting 10 | ) 11 | for repo in $external_plugins; do 12 | if [[ ! -d $ZSH_CUSTOM/plugins/${repo:t} ]]; then 13 | git clone https://github.com/${repo} $ZSH_CUSTOM/plugins/${repo:t} 14 | fi 15 | done 16 | 17 | # set your normal oh-my-zsh plugins 18 | plugins=( 19 | git 20 | magic-enter 21 | ) 22 | 23 | # now add your external plugins to your OMZ plugins list 24 | plugins+=(${external_plugins:t}) 25 | 26 | # then source oh-my-zsh 27 | source $ZSH/oh-my-zsh.sh 28 | -------------------------------------------------------------------------------- /examples/ohmyzsh_zshrc.zsh: -------------------------------------------------------------------------------- 1 | # clone a plugin, identify its init file, source it, and add it to your fpath 2 | function plugin-load { 3 | local repo plugdir initfile initfiles=() 4 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 5 | for repo in $@; do 6 | plugdir=$ZPLUGINDIR/${repo:t} 7 | initfile=$plugdir/${repo:t}.plugin.zsh 8 | if [[ ! -d $plugdir ]]; then 9 | echo "Cloning $repo..." 10 | git clone -q --depth 1 --recursive --shallow-submodules https://github.com/$repo $plugdir 11 | fi 12 | if [[ ! -e $initfile ]]; then 13 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 14 | (( $#initfiles )) || { echo >&2 "No init file found '$repo'." && continue } 15 | ln -sf $initfiles[1] $initfile 16 | fi 17 | fpath+=$plugdir 18 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 19 | done 20 | } 21 | 22 | # OMZ expects a list named 'plugins' so we can't use that variable name for repos, 23 | # it can only contain the names of actual OMZ plugins 24 | plugins=( 25 | git 26 | copypath 27 | extract 28 | magic-enter 29 | ) 30 | 31 | # since we're using OMZ, we can even use one of its themes! 32 | ZSH_THEME=avit 33 | 34 | # NOW, here's the trick - let's use the name 'repos' for our plugin-load function instead of 'plugins'. 35 | # that will let OMZ exist with our 3rd party plugins 36 | repos=( 37 | ohmyzsh/ohmyzsh 38 | zsh-users/zsh-autosuggestions 39 | zsh-users/zsh-history-substring-search 40 | zdharma-continuum/fast-syntax-highlighting 41 | ) 42 | plugin-load $repos 43 | -------------------------------------------------------------------------------- /examples/prezto_zshrc.zsh: -------------------------------------------------------------------------------- 1 | # prezto calls plugins 'contribs', so let's match that verbiage 2 | 3 | # clone contribs and identify their init files 4 | # let prezto source them later 5 | function contrib-clone() { 6 | local repo plugin_name plugin_dir initfile initfiles=() 7 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 8 | for repo in $@; do 9 | plugin_name=${repo:t} 10 | plugin_dir=$ZPLUGINDIR/$plugin_name 11 | initfile=$plugin_dir/init.zsh 12 | if [[ ! -d $plugin_dir ]]; then 13 | echo "Cloning $repo" 14 | git clone -q --depth 1 --recursive --shallow-submodules \ 15 | https://github.com/$repo $plugin_dir 16 | fi 17 | if [[ ! -e $initfile ]]; then 18 | initfiles=($plugin_dir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 19 | (( $#initfiles )) && ln -sf $initfiles[1] $initfile 20 | fi 21 | done 22 | } 23 | 24 | # put our plugins in a .zcontribs dir 25 | ZPLUGINDIR=${ZDOTDIR:-~}/.zcontribs 26 | 27 | contribs=( 28 | # prezto 29 | sorin-ionescu/prezto 30 | belak/prezto-contrib 31 | 32 | # 3rd party contribs 33 | joshskidmore/zsh-fzf-history-search 34 | mattmc3/zman 35 | peterhurford/up.zsh 36 | rummik/zsh-tailf 37 | rupa/z 38 | zdharma-continuum/fast-syntax-highlighting 39 | ) 40 | contrib-clone $contribs 41 | 42 | prezto_modules=( 43 | # zprezto built-ins 44 | autosuggestions 45 | environment 46 | terminal 47 | editor 48 | directory 49 | spectrum 50 | utility 51 | history-substring-search 52 | prompt 53 | git 54 | 55 | # belak/prezto-contrib 56 | clipboard 57 | elapsed-time 58 | 59 | # 3rd party contribs 60 | up.zsh 61 | zsh-tailf 62 | zsh-fzf-history-search 63 | z 64 | zman 65 | 66 | # load these last 67 | completion 68 | fast-syntax-highlighting 69 | ) 70 | 71 | # this Prezto config section can go in .zpreztorc 72 | # or you can keep it all in .zshrc if you don't want an extra Prezto file 73 | zstyle ':prezto:load' pmodule $prezto_modules 74 | zstyle ':prezto:load' pmodule-dirs $ZPLUGINDIR $ZPLUGINDIR/prezto-contrib 75 | zstyle ':prezto:load' pmodule-allow-overrides 'yes' 76 | zstyle ':prezto:module:prompt' theme 'sorin' 77 | zstyle ':prezto:module:git:alias' skip 'yes' 78 | 79 | # source prezto and let it load the contribs we cloned and added to modules 80 | ZPREZTODIR=$ZPLUGINDIR/prezto 81 | source $ZPREZTODIR/init.zsh 82 | -------------------------------------------------------------------------------- /examples/zshrc.zsh: -------------------------------------------------------------------------------- 1 | # where should we download your Zsh plugins? 2 | #ZPLUGINDIR=$ZDOTDIR/plugins 3 | 4 | ##? Clone a plugin, identify its init file, source it, and add it to your fpath. 5 | function plugin-load { 6 | local repo plugdir initfile initfiles=() 7 | : ${ZPLUGINDIR:=${ZDOTDIR:-~/.config/zsh}/plugins} 8 | for repo in $@; do 9 | plugdir=$ZPLUGINDIR/${repo:t} 10 | initfile=$plugdir/${repo:t}.plugin.zsh 11 | if [[ ! -d $plugdir ]]; then 12 | echo "Cloning $repo..." 13 | git clone -q --depth 1 --recursive --shallow-submodules \ 14 | https://github.com/$repo $plugdir 15 | fi 16 | if [[ ! -e $initfile ]]; then 17 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 18 | (( $#initfiles )) || { echo >&2 "No init file found '$repo'." && continue } 19 | ln -sf $initfiles[1] $initfile 20 | fi 21 | fpath+=$plugdir 22 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 23 | done 24 | } 25 | 26 | # make a github repo plugins list 27 | plugins=( 28 | sindresorhus/pure 29 | mattmc3/zman 30 | rupa/z 31 | rummik/zsh-tailf 32 | peterhurford/up.zsh 33 | zsh-users/zsh-autosuggestions 34 | zsh-users/zsh-history-substring-search 35 | 36 | # load these at hypersonic load speeds with zsh-defer 37 | romkatv/zsh-defer 38 | olets/zsh-abbr 39 | zdharma-continuum/fast-syntax-highlighting 40 | ) 41 | plugin-load $plugins 42 | -------------------------------------------------------------------------------- /examples/zshrc_clone.zsh: -------------------------------------------------------------------------------- 1 | # You can also separate the clone and load portions for more advanced plugin loads 2 | 3 | # where should we download your Zsh plugins? 4 | ZPLUGINDIR=${ZDOTDIR:-$HOME/.config/zsh}/plugins 5 | 6 | # declare a simple plugin-clone function, leaving the user to load plugins themselves 7 | function plugin-clone { 8 | local repo plugdir initfile initfiles=() 9 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 10 | for repo in $@; do 11 | plugdir=$ZPLUGINDIR/${repo:t} 12 | initfile=$plugdir/${repo:t}.plugin.zsh 13 | if [[ ! -d $plugdir ]]; then 14 | echo "Cloning $repo..." 15 | git clone -q --depth 1 --recursive --shallow-submodules https://github.com/$repo $plugdir 16 | fi 17 | if [[ ! -e $initfile ]]; then 18 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 19 | (( $#initfiles )) && ln -sf $initfiles[1] $initfile 20 | fi 21 | done 22 | } 23 | 24 | function plugin-source { 25 | local plugdir initfile 26 | ZPLUGINDIR=${ZPLUGINDIR:-${ZDOTDIR:-$HOME/.config/zsh}/plugins} 27 | for plugdir in $@; do 28 | [[ $plugdir = /* ]] || plugdir=$ZPLUGINDIR/$plugdir 29 | fpath+=$plugdir 30 | initfile=$plugdir/${plugdir:t}.plugin.zsh 31 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 32 | done 33 | } 34 | 35 | # make a github repo plugins list 36 | repos=( 37 | # not-sourcable plugins 38 | romkatv/zsh-bench 39 | 40 | # projects with nested plugins 41 | belak/zsh-utils 42 | ohmyzsh/ohmyzsh 43 | 44 | # regular plugins 45 | zsh-users/zsh-autosuggestions 46 | zsh-users/zsh-history-substring-search 47 | zdharma-continuum/fast-syntax-highlighting 48 | ) 49 | plugin-clone $repos 50 | 51 | # handle non-standard plugins 52 | export PATH="$ZPLUGINDIR/zsh-bench:$PATH" 53 | for file in $ZPLUGINDIR/ohmyzsh/lib/*.zsh; do 54 | source $file 55 | done 56 | 57 | # source other plugins 58 | plugins=( 59 | zsh-utils/history 60 | zsh-utils/complete 61 | zsh-utils/utility 62 | ohmyzsh/plugins/magic-enter 63 | ohmyzsh/plugins/history-substring-search 64 | ohmyzsh/plugins/z 65 | fast-syntax-highlighting 66 | zsh-autosuggestions 67 | ) 68 | plugin-source $plugins 69 | -------------------------------------------------------------------------------- /tests/__init__.zsh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | function t_setup { 3 | emulate -L zsh 4 | setopt local_options 5 | 0=${(%):-%x} 6 | 7 | # save fpath 8 | typeset -g T_PREV_FPATH=( $fpath ) 9 | 10 | # mock git 11 | # function git { echo git "$@" } 12 | 13 | # works with BSD and GNU gmktemp 14 | T_TEMPDIR=${$(mktemp -d -t zsh_unplugged.XXXXXXXX):A} 15 | 16 | # put zdotdir in position 17 | mkdir -p $T_TEMPDIR/plugins 18 | typeset -g OLD_ZDOTDIR=$ZDOTDIR 19 | export ZDOTDIR=$T_TEMPDIR/zdotdir 20 | typeset -g OLD_XDG_DATA_HOME=$XDG_DATA_HOME 21 | export XDG_DATA_HOME=$T_TEMPDIR/.local/share 22 | 23 | # add unplugged 24 | mkdir -p $ZDOTDIR/.unplugged 25 | cp ${0:A:h:h}/*.zsh $ZDOTDIR/.unplugged 26 | } 27 | 28 | function t_teardown { 29 | emulate -L zsh 30 | setopt local_options 31 | 0=${(%):-%x} 32 | 33 | # reset current session 34 | export ZDOTDIR=$OLD_ZDOTDIR 35 | export XDG_DATA_HOME=$OLD_XDG_DATA_HOME 36 | unset ZPLUGINDIR ZUNPLUG_CUSTOM ZUNPLUG_REPOS 37 | 38 | # restore original fpath 39 | fpath=( $T_PREV_FPATH ) 40 | 41 | # unfunction 42 | for funcname in clone load compile update; do 43 | (( $+functions[plugin-$funcname] )) && unfunction plugin-${funcname} 44 | done 45 | for funcname in zsh-defer; do 46 | (( $+functions[$funcname] )) && unfunction ${funcname} 47 | done 48 | 49 | # remove tempdir 50 | [[ -d "$T_TEMPDIR" ]] && rm -rf -- "$T_TEMPDIR" 51 | } 52 | 53 | function substenv { 54 | if (( $# == 0 )); then 55 | substenv ZDOTDIR | substenv HOME 56 | else 57 | local sedexp="s|${(P)1}|\$$1|g" 58 | shift 59 | command sed "$sedexp" "$@" 60 | fi 61 | } 62 | 63 | function mockgit { 64 | } 65 | -------------------------------------------------------------------------------- /tests/run-clitests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 0=${(%):-%x} 3 | setopt extended_glob 4 | 5 | cd ${0:A:h:h} 6 | 7 | local o_unit 8 | zparseopts -D -M -- -unit=o_unit || return 1 9 | 10 | testfiles=() 11 | if (( $# > 0 )); then 12 | testfiles=($@) 13 | elif (( $#o_unit )); then 14 | testfiles=($PWD/tests/*.md~*test-real*~*foo*) 15 | else 16 | testfiles=($PWD/tests/*.md) 17 | fi 18 | 19 | # foo example test command 20 | # env -i PATH=$PATH FPATH=$FPATH \ 21 | # zsh -f -- =clitest --list-run --progress dot --prompt '%' --color always $PWD/tests/foo.md 22 | 23 | env -i PATH=$PATH FPATH=$FPATH PAGER=cat \ 24 | zsh -f -- \ 25 | =clitest \ 26 | --list-run --progress dot --prompt '%' \ 27 | --color always \ 28 | --pre-flight 'git --version; print $PWD $VENDOR $OSTYPE =zsh $ZSH_VERSION $ZSH_PATCHLEVEL' \ 29 | -- $testfiles 30 | -------------------------------------------------------------------------------- /tests/test-advanced-zshrc.md: -------------------------------------------------------------------------------- 1 | # test-zsh-unplugged 2 | 3 | ## Setup 4 | 5 | ```zsh 6 | % source ./tests/__init__.zsh 7 | % t_setup 8 | % 9 | ``` 10 | 11 | ## Test 12 | 13 | TODO 14 | 15 | ## Teardown 16 | 17 | ```zsh 18 | % t_teardown 19 | % 20 | ``` 21 | -------------------------------------------------------------------------------- /tests/test-unplugged.md: -------------------------------------------------------------------------------- 1 | # test-zsh-unplugged 2 | 3 | ## Setup 4 | 5 | ```zsh 6 | % source ./tests/__init__.zsh 7 | % t_setup 8 | % 9 | ``` 10 | 11 | ## Test 12 | 13 | plugin-load function does not exist 14 | ```zsh 15 | % echo $+functions[plugin-load] 16 | 0 17 | % 18 | ``` 19 | 20 | unplugged sources successfully 21 | ```zsh 22 | % source unplugged.zsh #=> --exit 0 23 | % echo $+functions[plugin-load] 24 | 1 25 | % 26 | ``` 27 | 28 | zsh_unplugged clones a plugin 29 | ```zsh 30 | % echo $+functions[zsh-defer] 31 | 0 32 | % plugin-load romkatv/zsh-defer 33 | Cloning romkatv/zsh-defer... 34 | % echo $+functions[zsh-defer] 35 | 1 36 | % 37 | ``` 38 | 39 | ## Teardown 40 | 41 | ```zsh 42 | % t_teardown 43 | % 44 | ``` 45 | -------------------------------------------------------------------------------- /tests/test-zsh-unplugged.md: -------------------------------------------------------------------------------- 1 | # test-zsh-unplugged 2 | 3 | ## Setup 4 | 5 | ```zsh 6 | % source ./tests/__init__.zsh 7 | % t_setup 8 | % 9 | ``` 10 | 11 | ## Test 12 | 13 | plugin-load function does not exist 14 | ```zsh 15 | % echo $+functions[plugin-load] 16 | 0 17 | % 18 | ``` 19 | 20 | zsh_unplugged sources successfully 21 | ```zsh 22 | % source zsh_unplugged.zsh #=> --exit 0 23 | % echo $+functions[plugin-load] 24 | 1 25 | % 26 | ``` 27 | 28 | zsh_unplugged clones a plugin 29 | ```zsh 30 | % echo $+functions[zsh-defer] 31 | 0 32 | % plugin-script romkatv/zsh-defer | substenv ZUNPLUG_REPOS 33 | Cloning romkatv/zsh-defer... 34 | source $ZUNPLUG_REPOS/romkatv/zsh-defer/zsh-defer.plugin.zsh 35 | % plugin-load romkatv/zsh-defer 36 | % echo $+functions[zsh-defer] 37 | 1 38 | % 39 | ``` 40 | 41 | ```zsh 42 | % echo $ZUNPLUG_REPOS | substenv XDG_DATA_HOME 43 | $XDG_DATA_HOME/zsh_unplugged 44 | % echo $ZUNPLUG_CUSTOM | substenv ZDOTDIR 45 | $ZDOTDIR/plugins 46 | % 47 | ``` 48 | 49 | ```zsh 50 | % setopt glob_dots extended_glob 51 | % zwcfiles=($ZUNPLUG_REPOS/**/*.zwc(N)) 52 | % echo $#zwcfiles 53 | 1 54 | % 55 | ``` 56 | 57 | ## Teardown 58 | 59 | ```zsh 60 | % t_teardown 61 | % 62 | ``` 63 | -------------------------------------------------------------------------------- /unplugged.zsh: -------------------------------------------------------------------------------- 1 | # backwards compat 2 | 0=${(%):-%N} 3 | source ${0:A:h}/zsh_unplugged.zsh 4 | -------------------------------------------------------------------------------- /zsh_unplugged.zsh: -------------------------------------------------------------------------------- 1 | # zsh_unplugged - https://github.com/mattmc3/zsh_unplugged 2 | # 3 | # A simple, fast, minimalist Zsh plugin management function in <20 lines of code. 4 | # 5 | # Usage: 6 | # ZPLUGINDIR=${ZDOTDIR:-~}/plugins 7 | # source $ZPLUGINDIR/zsh_unplugged/zsh_unplugged.zsh 8 | # repos=( 9 | # ... 10 | # zsh-users/zsh-syntax-highlighting 11 | # zsh-users/zsh-autosuggestions 12 | # zsh-users/zsh-history-substring-search 13 | # ) 14 | # plugin-load $repos 15 | # 16 | 17 | ##? Clone a plugin, identify its init file, source it, and add it to your fpath. 18 | function plugin-load { 19 | local repo plugdir initfile initfiles=() 20 | : ${ZPLUGINDIR:=${ZDOTDIR:-~/.config/zsh}/plugins} 21 | for repo in $@; do 22 | plugdir=$ZPLUGINDIR/${repo:t} 23 | initfile=$plugdir/${repo:t}.plugin.zsh 24 | if [[ ! -d $plugdir ]]; then 25 | echo "Cloning $repo..." 26 | git clone -q --depth 1 --recursive --shallow-submodules \ 27 | https://github.com/$repo $plugdir 28 | fi 29 | if [[ ! -e $initfile ]]; then 30 | initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) 31 | (( $#initfiles )) || { echo >&2 "No init file found '$repo'." && continue } 32 | ln -sf $initfiles[1] $initfile 33 | fi 34 | fpath+=$plugdir 35 | (( $+functions[zsh-defer] )) && zsh-defer . $initfile || . $initfile 36 | done 37 | } 38 | --------------------------------------------------------------------------------