├── .gitignore ├── .gitmodules ├── .zshrc ├── README.adoc ├── UNLICENSE ├── completions └── _autosource ├── examples ├── source │ └── enable-shared-history.zsh └── zshrc.local ├── functions ├── autosource ├── brpaste ├── sourceall └── sprunge ├── plugins ├── sudo └── xterm-title ├── prompts ├── async ├── prompt_pure_setup ├── prompt_purer_setup ├── prompt_shellder_setup └── prompt_toasty_setup ├── source ├── aliases.zsh ├── bindkeys.zsh ├── completions.zsh ├── env.zsh └── options.zsh ├── third-party-licenses ├── LICENSE.OMZ.md ├── LICENSE.pure.txt ├── LICENSE.purer.txt └── LICENSE.shellder.txt ├── zshenv └── zshrc /.gitignore: -------------------------------------------------------------------------------- 1 | *.zwc 2 | .zcompdump 3 | .zdirs 4 | .zprofile 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CosmicToast/toasty-zsh/5e556ccae776381ce443598610675dd025529f83/.gitmodules -------------------------------------------------------------------------------- /.zshrc: -------------------------------------------------------------------------------- 1 | zshrc -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | :icons: font 2 | :source-highlighter: pygments 3 | :toc: preamble 4 | 5 | // Links 6 | :license: LICENSE.md 7 | :example: examples/zshrc.local 8 | 9 | :img-license: https://img.shields.io/github/license/5pacetoast/toasty-zsh.svg 10 | 11 | :license-omz: third-party-licenses/LICENSE.OMZ.md 12 | :license-pure: third-party-licenses/LICENSE.pure.txt 13 | :license-purer: third-party-licenses/LICENSE.purer.txt 14 | :license-shellder: third-party-licenses/LICENSE.shellder.txt 15 | 16 | :repo-omz: https://github.com/robbyrussell/oh-my-zsh[Oh My Zsh] 17 | :repo-pure: https://github.com/sindresorhus/pure[Pure] 18 | :repo-purer: https://github.com/dfurnes/purer[Purer] 19 | :repo-shellder: https://github.com/simnalamburt/shellder[Shellder] 20 | 21 | :brpaste: https://brpaste.xyz[BRPaste] 22 | :sprunge: http://sprunge.us[Sprunge] 23 | :zshwiki: http://zshwiki.org[Zsh Wiki] 24 | 25 | // Github-specific workarounds 26 | ifdef::env-github[] 27 | :tip-caption: :bulb: 28 | :note-caption: :information_source: 29 | :important-caption: :heavy_exclamation_mark: 30 | :caution-caption: :fire: 31 | :warning-caption: :warning: 32 | endif::[] 33 | 34 | = Toasty Zsh 35 | 36 | image::{img-license}[License, link={license}] 37 | 38 | The zsh framework made to facilitate management, not dictate it. 39 | 40 | == Quickstart 41 | 1. Clone this repository somewhere 42 | 2. `echo ZDOTDIR=/path/to/repo > ~/.zshenv` 43 | 3. Restart your z shell 44 | 45 | === Expanding on it 46 | - Drop files you want to be sourced during startup into `$zd/source/` 47 | - Drop functions you want to potentially use/autoload into `$zd/functions/` 48 | - Drop plugins you want to source on-demand into `$zd/plugins/` 49 | - Drop custom completions into `$zd/completions/` 50 | - Drop custom prompts into `$zd/prompts/` 51 | 52 | NOTE: Most of these are for mindspace separation, the completions, prompts and functions directories are functionally identical, and as such you can use any of them for any purpose. 53 | 54 | WARNING: Don't run compinit yourself! It happens at the very end (after sourcing `zshrc.local`) as is. All it'll do is make your performance worse. 55 | 56 | NOTE: If you want `$zd` to be anywhere but `~/.zsh`, set it in `~/.zshenv`. 57 | 58 | === Full List of Supported Installation Methods 59 | ==== Local 60 | - Setting `$ZDOTDIR` (usually in `~/.zshenv`) to point to the repository. 61 | + 62 | WARNING: This may, however, dirty your repository, depending on what `$ZDOTDIR` ends up being used for. 63 | - Sourcing `zshrc` in `~/.zshrc`. 64 | - Linking `zshrc` to `~/.zshrc`. 65 | 66 | ==== Global 67 | - Cloning toasty-zsh into `/etc/zsh` (on distributions with sane/common zsh defaults). 68 | - Sourcing `zshrc` in `/etc/zsh/zshrc`. 69 | - Linking `zshrc` to `/etc/zsh/zshrc`. 70 | 71 | WARNING: You may want to `touch ~/.zshrc` when installing globally, to avoid zsh complaining about it being missing. 72 | 73 | NOTE: If you have a global install of toasty-zsh, or some other framework, and have a local version, you can `setopt NO_GLOBAL_RCS` in `~/.zshenv` to skip loading the global variant. 74 | 75 | == Structure 76 | Toasty Zsh provides a structure under `~/.zsh` for you to use. Everything except `zshrc.local` is optional. 77 | 78 | `$zshd`:: Dynamically set to wherever the Toasty Zsh repository copy is. 79 | `$zd`:: Your "Z Directory". Set to `~/.zsh` by default. The rest of this list is inside of this. 80 | completions/:: Added to your `$fpath`. Serves to put custom completions into without muddying up your functions. 81 | functions/:: Added to your `$fpath`. Serves to put your functions into, for autoloading. 82 | plugins/:: Added to your `$spath`. Serves for optionally-sourced plugins. See <> for more details. 83 | prompts/:: Added to your `$fpath`. Serves to throw your custom prompts into without muddying up your functions. 84 | source/:: Added to your `$apath`. Serves for automatic sourcing on-demand/startup. See <> for more details. 85 | pre:: Sourced right before running <>, giving you a chance to edit the sane-default `${fpath,spath,apath}` values. 86 | zshrc.local:: Sourced right before compinit. 87 | 88 | == API 89 | === Functions 90 | Various functions for use. Some are used internally. 91 | 92 | NOTE: Enabling (autoloading) a function in zsh defers loading - it makes the function available from the moment you do it, but it won't be actually loaded until you first use it, making them much more efficient than sourced files. 93 | 94 | ==== Autosource 95 | Iterates over `$spath` until it finds a matching `$1`, then sources it. 96 | 97 | Examples: 98 | [source,sh] 99 | ---- 100 | spath=( "$PWD" ) autosource a # <1> 101 | spath=( a b ) autosource c # <2> 102 | spath=( "$PWD" ) autosource foo/bar # <3> 103 | ---- 104 | <1> will source `./a` if it exists 105 | <2> will source `./a/c`, and then `./b/c` if that fails 106 | <3> will source `./foo/bar` if it exists 107 | 108 | WARNING: Relative entries will always be relative. If you set `$spath` to `.` and then change directories, it'll continue using `.`, which is potentially insecure. 109 | 110 | NOTE: For convenience's sake, `$spath` and `$SPATH` are bound - you can manipulate `$SPATH` (which is formatted like `$PATH`) to modify `$spath`. 111 | 112 | ==== Sourceall 113 | Will source every file in every directory in `$apath`. If an argument is provided, will only source files that end in `.$1`. 114 | 115 | Examples: 116 | [source,sh] 117 | ---- 118 | apath=( a b ) sourceall # <1> 119 | apath=( "$PWD" ) sourceall zsh # <2> 120 | ---- 121 | <1> will source `a/*` and `b/*` 122 | <2> will source `./*.zsh` 123 | 124 | NOTE: By default, Toasty Zsh will run `sourceall zsh` between sourcing `$zd/pre` and `zshrc.local`. By default, it only goes through `$zshd/source` and `$zd/source`. You can customize this behavior in `$zd/pre`. 125 | 126 | WARNING: Sourceall does not recurse into subdirectories, though you can work around that by adding a `99-subdir.zsh` file or similar where you call it with a custom `$apath` set. 127 | 128 | NOTE: As with `$spath`, `$apath` is bound to a `$PATH`-like `$APATH`. 129 | 130 | ==== Brpaste 131 | Simple wrapper around {brpaste}. 132 | Takes thing in stdin, or uses the first argument as the file to upload. 133 | Outputs the paste url. 134 | 135 | Examples: 136 | [source,sh] 137 | ---- 138 | echo hi | brpaste # <1> 139 | brpaste file # <2> 140 | bsdtar -cf- --format shar dir | brpaste # <3> 141 | brpaste file | xsel -ib # <4> 142 | ---- 143 | <1> upload "hi\n" to brpaste.xyz 144 | <2> upload file to brpaste.xyz 145 | <3> upload a "shar" archive of dir to brpaste.xyz 146 | <4> upload file to brpaste.xyz and place the non-raw link into the clipboard 147 | 148 | ==== Sprunge 149 | Simple wrapper around {sprunge}. Takes things in stdin, outputs the url into stdout. 150 | 151 | Examples: 152 | [source,sh] 153 | ---- 154 | echo hi | sprunge # <1> 155 | sprunge < file # <2> 156 | bsdtar -cf - --format shar dir | sprunge # <3> 157 | ---- 158 | <1> upload `"hi\n"` to sprunge.us 159 | <2> upload file to sprunge.us 160 | <3> upload a "shar" archive of dir to sprunge.us 161 | 162 | === Plugins 163 | Plugins are just files you source! See <> for a convenient way to do so. 164 | 165 | You can add your own by dropping them into a directory in your `$spath` (such as `$zd/plugins`). 166 | 167 | ==== Sudo 168 | Press `` twice to either add or remove `sudo` from the beginning of your line. 169 | 170 | If the current line is empty, operates on the previous line instead. 171 | 172 | ==== Xterm-Title 173 | Sets up a simple hook system to print what's currently being executed into an xterm-compatible terminal's title. 174 | 175 | WARNING: some prompts (such as {repo-pure}) do this for you aleady! If you use both, they won't conflict, but you'd be wasting cycles *and* might see some strange text flashing through on every command. 176 | 177 | == External Projects 178 | I didn't write everything in here, some of it is bundled. 179 | 180 | Note that you do not pay (except with drive space) for most of these unless you choose to use them. 181 | 182 | === Plugins 183 | 184 | Sudo:: From {repo-omz}. 185 | 186 | === Prompts 187 | 188 | Pure:: From {repo-pure}. 189 | Purer:: From {repo-purer}. 190 | Shellder:: From {repo-shellder}. 191 | Toasty:: Written from scratch by me, but takes heavy inspiration from robbyrussel's theme from {repo-omz}. 192 | 193 | === Other 194 | 195 | `bindkeys.zsh`:: Written by me, but heavily inspired by similar content from {repo-omz} and the {zshwiki}. 196 | 197 | === Licenses 198 | 199 | - Oh My Zsh link:{license-omz}[LICENSE] 200 | - Pure link:{license-pure}[LICENSE] 201 | - Purer link:{license-purer}[LICENSE] 202 | - Shellder link:{license-shellder}[LICENSE] 203 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /completions/_autosource: -------------------------------------------------------------------------------- 1 | #compdef autosource 2 | 3 | function _autosource { 4 | _alternative \ 5 | 'plugin:filename:{_path_files -W spath}' 6 | } 7 | 8 | _autosource 9 | 10 | # vim: ft=zsh 11 | -------------------------------------------------------------------------------- /examples/source/enable-shared-history.zsh: -------------------------------------------------------------------------------- 1 | setopt sharehistory 2 | 3 | HISTSIZE=1000 4 | SAVEHIST=1000 5 | HISTFILE="$zd/history" 6 | -------------------------------------------------------------------------------- /examples/zshrc.local: -------------------------------------------------------------------------------- 1 | # example of a basic local zshrc 2 | 3 | autoload autosource # for plugins 4 | autosource sudo # enable sudo plugin 5 | 6 | autoload sprunge # something with output | sprunge, and sprunge < somefile 7 | 8 | # get a non-default prompt 9 | autoload -Uz promptinit 10 | promptinit 11 | prompt pure 12 | -------------------------------------------------------------------------------- /functions/autosource: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | local f= 3 | local p= 4 | for f 5 | do 6 | for p in $spath 7 | do 8 | [[ -r $p/$f ]] && . "$p/$f" && break 9 | done 10 | done 11 | -------------------------------------------------------------------------------- /functions/brpaste: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | host='https://brpaste.xyz' 3 | [[ $# -eq 0 ]] && set -- '-' 4 | brpaste_id=$(curl -\#fF "data=<$1" "$host") \ 5 | || { echo 'ERROR: Upload failed!' >&2 && return 1; } 6 | printf '%s/%s\n' "$host" "$brpaste_id" 7 | -------------------------------------------------------------------------------- /functions/sourceall: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | local ext= 3 | local f= 4 | local p= 5 | 6 | # calling `sourceall zsh` will source *.zsh, otherwise * 7 | (( $# > 0 )) && ext=".$1" 8 | 9 | for p in $apath 10 | do 11 | [[ -d $p ]] || continue 12 | for f in $p/*$ext(.N) 13 | do 14 | [[ -r $f ]] && . $f 15 | done 16 | done 17 | -------------------------------------------------------------------------------- /functions/sprunge: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | curl -F 'sprunge=<-' http://sprunge.us 3 | -------------------------------------------------------------------------------- /plugins/sudo: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Description 3 | # ----------- 4 | # 5 | # sudo will be inserted before the command 6 | # 7 | # ------------------------------------------------------------------------------ 8 | # Authors 9 | # ------- 10 | # 11 | # * Dongweiming 12 | # 13 | # ------------------------------------------------------------------------------ 14 | 15 | sudo-command-line() { 16 | [[ -z $BUFFER ]] && zle up-history 17 | if [[ $BUFFER == sudo\ * ]]; then 18 | LBUFFER="${LBUFFER#sudo }" 19 | else 20 | LBUFFER="sudo $LBUFFER" 21 | fi 22 | } 23 | zle -N sudo-command-line 24 | # Defined shortcut keys: [Esc] [Esc] 25 | bindkey "\e\e" sudo-command-line 26 | -------------------------------------------------------------------------------- /plugins/xterm-title: -------------------------------------------------------------------------------- 1 | function xterm-title { 2 | print -Pn '\e]0;%n@%m $(history $HISTCMD | cut -b8-)\a' 3 | } 4 | 5 | autoload -Uz add-zsh-hook 6 | add-zsh-hook precmd xterm-title 7 | add-zsh-hook preexec xterm-title 8 | add-zsh-hook chpwd xterm-title 9 | 10 | # vim: ft=zsh 11 | -------------------------------------------------------------------------------- /prompts/async: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | 3 | # 4 | # zsh-async 5 | # 6 | # version: 1.7.0 7 | # author: Mathias Fredriksson 8 | # url: https://github.com/mafredri/zsh-async 9 | # 10 | 11 | typeset -g ASYNC_VERSION=1.7.0 12 | # Produce debug output from zsh-async when set to 1. 13 | typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0} 14 | 15 | # Execute commands that can manipulate the environment inside the async worker. Return output via callback. 16 | _async_eval() { 17 | local ASYNC_JOB_NAME 18 | # Rename job to _async_eval and redirect all eval output to cat running 19 | # in _async_job. Here, stdout and stderr are not separated for 20 | # simplicity, this could be improved in the future. 21 | { 22 | eval "$@" 23 | } &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'cat') 24 | } 25 | 26 | # Wrapper for jobs executed by the async worker, gives output in parseable format with execution time 27 | _async_job() { 28 | # Disable xtrace as it would mangle the output. 29 | setopt localoptions noxtrace 30 | 31 | # Store start time for job. 32 | float -F duration=$EPOCHREALTIME 33 | 34 | # Run the command and capture both stdout (`eval`) and stderr (`cat`) in 35 | # separate subshells. When the command is complete, we grab write lock 36 | # (mutex token) and output everything except stderr inside the command 37 | # block, after the command block has completed, the stdin for `cat` is 38 | # closed, causing stderr to be appended with a $'\0' at the end to mark the 39 | # end of output from this job. 40 | local jobname=${ASYNC_JOB_NAME:-$1} 41 | local stdout stderr ret tok 42 | { 43 | stdout=$(eval "$@") 44 | ret=$? 45 | duration=$(( EPOCHREALTIME - duration )) # Calculate duration. 46 | 47 | # Grab mutex lock, stalls until token is available. 48 | read -r -k 1 -p tok || exit 1 49 | 50 | # Return output ( ). 51 | print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration 52 | } 2> >(stderr=$(cat) && print -r -n - " "${(q)stderr}$'\0') 53 | 54 | # Unlock mutex by inserting a token. 55 | print -n -p $tok 56 | } 57 | 58 | # The background worker manages all tasks and runs them without interfering with other processes 59 | _async_worker() { 60 | # Reset all options to defaults inside async worker. 61 | emulate -R zsh 62 | 63 | # Make sure monitor is unset to avoid printing the 64 | # pids of child processes. 65 | unsetopt monitor 66 | 67 | # Redirect stderr to `/dev/null` in case unforseen errors produced by the 68 | # worker. For example: `fork failed: resource temporarily unavailable`. 69 | # Some older versions of zsh might also print malloc errors (know to happen 70 | # on at least zsh 5.0.2 and 5.0.8) likely due to kill signals. 71 | exec 2>/dev/null 72 | 73 | # When a zpty is deleted (using -d) all the zpty instances created before 74 | # the one being deleted receive a SIGHUP, unless we catch it, the async 75 | # worker would simply exit (stop working) even though visible in the list 76 | # of zpty's (zpty -L). 77 | TRAPHUP() { 78 | return 0 # Return 0, indicating signal was handled. 79 | } 80 | 81 | local -A storage 82 | local unique=0 83 | local notify_parent=0 84 | local parent_pid=0 85 | local coproc_pid=0 86 | local processing=0 87 | 88 | local -a zsh_hooks zsh_hook_functions 89 | zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory) 90 | zsh_hook_functions=(${^zsh_hooks}_functions) 91 | unfunction $zsh_hooks &>/dev/null # Deactivate all zsh hooks inside the worker. 92 | unset $zsh_hook_functions # And hooks with registered functions. 93 | unset zsh_hooks zsh_hook_functions # Cleanup. 94 | 95 | child_exit() { 96 | local -a pids 97 | pids=(${${(v)jobstates##*:*:}%\=*}) 98 | 99 | # If coproc (cat) is the only child running, we close it to avoid 100 | # leaving it running indefinitely and cluttering the process tree. 101 | if (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then 102 | coproc : 103 | coproc_pid=0 104 | fi 105 | 106 | # On older version of zsh (pre 5.2) we notify the parent through a 107 | # SIGWINCH signal because `zpty` did not return a file descriptor (fd) 108 | # prior to that. 109 | if (( notify_parent )); then 110 | # We use SIGWINCH for compatibility with older versions of zsh 111 | # (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could 112 | # cause a deadlock in the shell under certain circumstances. 113 | kill -WINCH $parent_pid 114 | fi 115 | } 116 | 117 | # Register a SIGCHLD trap to handle the completion of child processes. 118 | trap child_exit CHLD 119 | 120 | # Process option parameters passed to worker 121 | while getopts "np:u" opt; do 122 | case $opt in 123 | n) notify_parent=1;; 124 | p) parent_pid=$OPTARG;; 125 | u) unique=1;; 126 | esac 127 | done 128 | 129 | killjobs() { 130 | local tok 131 | local -a pids 132 | pids=(${${(v)jobstates##*:*:}%\=*}) 133 | 134 | # No need to send SIGHUP if no jobs are running. 135 | (( $#pids == 0 )) && continue 136 | (( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue 137 | 138 | # Grab lock to prevent half-written output in case a child 139 | # process is in the middle of writing to stdin during kill. 140 | (( coproc_pid )) && read -r -k 1 -p tok 141 | 142 | kill -HUP -$$ # Send to entire process group. 143 | coproc : # Quit coproc. 144 | coproc_pid=0 # Reset pid. 145 | } 146 | 147 | local request do_eval=0 148 | local -a cmd 149 | while :; do 150 | # Wait for jobs sent by async_job. 151 | read -r -d $'\0' request || { 152 | # Since we handle SIGHUP above (and thus do not know when `zpty -d`) 153 | # occurs, a failure to read probably indicates that stdin has 154 | # closed. This is why we propagate the signal to all children and 155 | # exit manually. 156 | kill -HUP -$$ # Send SIGHUP to all jobs. 157 | exit 0 158 | } 159 | 160 | # Check for non-job commands sent to worker 161 | case $request in 162 | _unset_trap) notify_parent=0; continue;; 163 | _killjobs) killjobs; continue;; 164 | _async_eval*) do_eval=1;; 165 | esac 166 | 167 | # Parse the request using shell parsing (z) to allow commands 168 | # to be parsed from single strings and multi-args alike. 169 | cmd=("${(z)request}") 170 | 171 | # Name of the job (first argument). 172 | local job=$cmd[1] 173 | 174 | # If worker should perform unique jobs 175 | if (( unique )); then 176 | # Check if a previous job is still running, if yes, let it finnish 177 | for pid in ${${(v)jobstates##*:*:}%\=*}; do 178 | if [[ ${storage[$job]} == $pid ]]; then 179 | continue 2 180 | fi 181 | done 182 | fi 183 | 184 | # Guard against closing coproc from trap before command has started. 185 | processing=1 186 | 187 | # Because we close the coproc after the last job has completed, we must 188 | # recreate it when there are no other jobs running. 189 | if (( ! coproc_pid )); then 190 | # Use coproc as a mutex for synchronized output between children. 191 | coproc cat 192 | coproc_pid="$!" 193 | # Insert token into coproc 194 | print -n -p "t" 195 | fi 196 | 197 | if (( do_eval )); then 198 | shift cmd # Strip _async_eval from cmd. 199 | _async_eval $cmd 200 | do_eval=0 201 | else 202 | # Run job in background, completed jobs are printed to stdout. 203 | _async_job $cmd & 204 | # Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')... 205 | storage[$job]="$!" 206 | fi 207 | 208 | processing=0 # Disable guard. 209 | done 210 | } 211 | 212 | # 213 | # Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the 214 | # job name, return code, output and execution time and with minimal effort. 215 | # 216 | # If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job 217 | # name), non-zero return code and fifth argument describing the error (stderr). 218 | # 219 | # usage: 220 | # async_process_results 221 | # 222 | # callback_function is called with the following parameters: 223 | # $1 = job name, e.g. the function passed to async_job 224 | # $2 = return code 225 | # $3 = resulting stdout from execution 226 | # $4 = execution time, floating point e.g. 2.05 seconds 227 | # $5 = resulting stderr from execution 228 | # $6 = has next result in buffer (0 = buffer empty, 1 = yes) 229 | # 230 | async_process_results() { 231 | setopt localoptions unset noshwordsplit noksharrays noposixidentifiers noposixstrings 232 | 233 | local worker=$1 234 | local callback=$2 235 | local caller=$3 236 | local -a items 237 | local null=$'\0' data 238 | integer -l len pos num_processed has_next 239 | 240 | typeset -gA ASYNC_PROCESS_BUFFER 241 | 242 | # Read output from zpty and parse it if available. 243 | while zpty -r -t $worker data 2>/dev/null; do 244 | ASYNC_PROCESS_BUFFER[$worker]+=$data 245 | len=${#ASYNC_PROCESS_BUFFER[$worker]} 246 | pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). 247 | 248 | # Keep going until we find a NULL-character. 249 | if (( ! len )) || (( pos > len )); then 250 | continue 251 | fi 252 | 253 | while (( pos <= len )); do 254 | # Take the content from the beginning, until the NULL-character and 255 | # perform shell parsing (z) and unquoting (Q) as an array (@). 256 | items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}") 257 | 258 | # Remove the extracted items from the buffer. 259 | ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]} 260 | 261 | len=${#ASYNC_PROCESS_BUFFER[$worker]} 262 | if (( len > 1 )); then 263 | pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). 264 | fi 265 | 266 | has_next=$(( len != 0 )) 267 | if (( $#items == 5 )); then 268 | items+=($has_next) 269 | $callback "${(@)items}" # Send all parsed items to the callback. 270 | (( num_processed++ )) 271 | elif [[ -z $items ]]; then 272 | # Empty items occur between results due to double-null ($'\0\0') 273 | # caused by commands being both pre and suffixed with null. 274 | else 275 | # In case of corrupt data, invoke callback with *async* as job 276 | # name, non-zero exit status and an error message on stderr. 277 | $callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next 278 | fi 279 | done 280 | done 281 | 282 | (( num_processed )) && return 0 283 | 284 | # Avoid printing exit value when `setopt printexitvalue` is active.` 285 | [[ $caller = trap || $caller = watcher ]] && return 0 286 | 287 | # No results were processed 288 | return 1 289 | } 290 | 291 | # Watch worker for output 292 | _async_zle_watcher() { 293 | setopt localoptions noshwordsplit 294 | typeset -gA ASYNC_PTYS ASYNC_CALLBACKS 295 | local worker=$ASYNC_PTYS[$1] 296 | local callback=$ASYNC_CALLBACKS[$worker] 297 | 298 | if [[ -n $callback ]]; then 299 | async_process_results $worker $callback watcher 300 | fi 301 | } 302 | 303 | # 304 | # Start a new asynchronous job on specified worker, assumes the worker is running. 305 | # 306 | # usage: 307 | # async_job [] 308 | # 309 | async_job() { 310 | setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings 311 | 312 | local worker=$1; shift 313 | 314 | local -a cmd 315 | cmd=("$@") 316 | if (( $#cmd > 1 )); then 317 | cmd=(${(q)cmd}) # Quote special characters in multi argument commands. 318 | fi 319 | 320 | # Quote the cmd in case RC_EXPAND_PARAM is set. 321 | zpty -w $worker "$cmd"$'\0' 322 | } 323 | 324 | # 325 | # Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example, 326 | # issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs. 327 | # 328 | # Output will be returned via callback, job name will be [async/eval]. 329 | # 330 | # usage: 331 | # async_worker_eval [] 332 | # 333 | async_worker_eval() { 334 | setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings 335 | 336 | local worker=$1; shift 337 | 338 | local -a cmd 339 | cmd=("$@") 340 | if (( $#cmd > 1 )); then 341 | cmd=(${(q)cmd}) # Quote special characters in multi argument commands. 342 | fi 343 | 344 | # Quote the cmd in case RC_EXPAND_PARAM is set. 345 | zpty -w $worker "_async_eval $cmd"$'\0' 346 | } 347 | 348 | # This function traps notification signals and calls all registered callbacks 349 | _async_notify_trap() { 350 | setopt localoptions noshwordsplit 351 | 352 | local k 353 | for k in ${(k)ASYNC_CALLBACKS}; do 354 | async_process_results $k ${ASYNC_CALLBACKS[$k]} trap 355 | done 356 | } 357 | 358 | # 359 | # Register a callback for completed jobs. As soon as a job is finnished, async_process_results will be called with the 360 | # specified callback function. This requires that a worker is initialized with the -n (notify) option. 361 | # 362 | # usage: 363 | # async_register_callback 364 | # 365 | async_register_callback() { 366 | setopt localoptions noshwordsplit nolocaltraps 367 | 368 | typeset -gA ASYNC_CALLBACKS 369 | local worker=$1; shift 370 | 371 | ASYNC_CALLBACKS[$worker]="$*" 372 | 373 | # Enable trap when the ZLE watcher is unavailable, allows 374 | # workers to notify (via -n) when a job is done. 375 | if [[ ! -o interactive ]] || [[ ! -o zle ]]; then 376 | trap '_async_notify_trap' WINCH 377 | fi 378 | } 379 | 380 | # 381 | # Unregister the callback for a specific worker. 382 | # 383 | # usage: 384 | # async_unregister_callback 385 | # 386 | async_unregister_callback() { 387 | typeset -gA ASYNC_CALLBACKS 388 | 389 | unset "ASYNC_CALLBACKS[$1]" 390 | } 391 | 392 | # 393 | # Flush all current jobs running on a worker. This will terminate any and all running processes under the worker, use 394 | # with caution. 395 | # 396 | # usage: 397 | # async_flush_jobs 398 | # 399 | async_flush_jobs() { 400 | setopt localoptions noshwordsplit 401 | 402 | local worker=$1; shift 403 | 404 | # Check if the worker exists 405 | zpty -t $worker &>/dev/null || return 1 406 | 407 | # Send kill command to worker 408 | async_job $worker "_killjobs" 409 | 410 | # Clear the zpty buffer. 411 | local junk 412 | if zpty -r -t $worker junk '*'; then 413 | (( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}" 414 | while zpty -r -t $worker junk '*'; do 415 | (( ASYNC_DEBUG )) && print -n "${(V)junk}" 416 | done 417 | (( ASYNC_DEBUG )) && print 418 | fi 419 | 420 | # Finally, clear the process buffer in case of partially parsed responses. 421 | typeset -gA ASYNC_PROCESS_BUFFER 422 | unset "ASYNC_PROCESS_BUFFER[$worker]" 423 | } 424 | 425 | # 426 | # Start a new async worker with optional parameters, a worker can be told to only run unique tasks and to notify a 427 | # process when tasks are complete. 428 | # 429 | # usage: 430 | # async_start_worker [-u] [-n] [-p ] 431 | # 432 | # opts: 433 | # -u unique (only unique job names can run) 434 | # -n notify through SIGWINCH signal 435 | # -p pid to notify (defaults to current pid) 436 | # 437 | async_start_worker() { 438 | setopt localoptions noshwordsplit 439 | 440 | local worker=$1; shift 441 | zpty -t $worker &>/dev/null && return 442 | 443 | typeset -gA ASYNC_PTYS 444 | typeset -h REPLY 445 | typeset has_xtrace=0 446 | 447 | # Make sure async worker is started without xtrace 448 | # (the trace output interferes with the worker). 449 | [[ -o xtrace ]] && { 450 | has_xtrace=1 451 | unsetopt xtrace 452 | } 453 | 454 | if (( ! ASYNC_ZPTY_RETURNS_FD )) && [[ -o interactive ]] && [[ -o zle ]]; then 455 | # When zpty doesn't return a file descriptor (on older versions of zsh) 456 | # we try to guess it anyway. 457 | integer -l zptyfd 458 | exec {zptyfd}>&1 # Open a new file descriptor (above 10). 459 | exec {zptyfd}>&- # Close it so it's free to be used by zpty. 460 | fi 461 | 462 | zpty -b $worker _async_worker -p $$ $@ || { 463 | async_stop_worker $worker 464 | return 1 465 | } 466 | 467 | # Re-enable it if it was enabled, for debugging. 468 | (( has_xtrace )) && setopt xtrace 469 | 470 | if [[ $ZSH_VERSION < 5.0.8 ]]; then 471 | # For ZSH versions older than 5.0.8 we delay a bit to give 472 | # time for the worker to start before issuing commands, 473 | # otherwise it will not be ready to receive them. 474 | sleep 0.001 475 | fi 476 | 477 | if [[ -o interactive ]] && [[ -o zle ]]; then 478 | if (( ! ASYNC_ZPTY_RETURNS_FD )); then 479 | REPLY=$zptyfd # Use the guessed value for the file desciptor. 480 | fi 481 | 482 | ASYNC_PTYS[$REPLY]=$worker # Map the file desciptor to the worker. 483 | zle -F $REPLY _async_zle_watcher # Register the ZLE handler. 484 | 485 | # Disable trap in favor of ZLE handler when notify is enabled (-n). 486 | async_job $worker _unset_trap 487 | fi 488 | } 489 | 490 | # 491 | # Stop one or multiple workers that are running, all unfetched and incomplete work will be lost. 492 | # 493 | # usage: 494 | # async_stop_worker [] 495 | # 496 | async_stop_worker() { 497 | setopt localoptions noshwordsplit 498 | 499 | local ret=0 worker k v 500 | for worker in $@; do 501 | # Find and unregister the zle handler for the worker 502 | for k v in ${(@kv)ASYNC_PTYS}; do 503 | if [[ $v == $worker ]]; then 504 | zle -F $k 505 | unset "ASYNC_PTYS[$k]" 506 | fi 507 | done 508 | async_unregister_callback $worker 509 | zpty -d $worker 2>/dev/null || ret=$? 510 | 511 | # Clear any partial buffers. 512 | typeset -gA ASYNC_PROCESS_BUFFER 513 | unset "ASYNC_PROCESS_BUFFER[$worker]" 514 | done 515 | 516 | return $ret 517 | } 518 | 519 | # 520 | # Initialize the required modules for zsh-async. To be called before using the zsh-async library. 521 | # 522 | # usage: 523 | # async_init 524 | # 525 | async_init() { 526 | (( ASYNC_INIT_DONE )) && return 527 | typeset -g ASYNC_INIT_DONE=1 528 | 529 | zmodload zsh/zpty 530 | zmodload zsh/datetime 531 | 532 | # Check if zsh/zpty returns a file descriptor or not, 533 | # shell must also be interactive with zle enabled. 534 | typeset -g ASYNC_ZPTY_RETURNS_FD=0 535 | [[ -o interactive ]] && [[ -o zle ]] && { 536 | typeset -h REPLY 537 | zpty _async_test : 538 | (( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1 539 | zpty -d _async_test 540 | } 541 | } 542 | 543 | async() { 544 | async_init 545 | } 546 | 547 | async "$@" 548 | -------------------------------------------------------------------------------- /prompts/prompt_pure_setup: -------------------------------------------------------------------------------- 1 | # Pure 2 | # by Sindre Sorhus 3 | # https://github.com/sindresorhus/pure 4 | # MIT License 5 | 6 | # For my own and others sanity 7 | # git: 8 | # %b => current branch 9 | # %a => current action (rebase/merge) 10 | # prompt: 11 | # %F => color dict 12 | # %f => reset color 13 | # %~ => current path 14 | # %* => time 15 | # %n => username 16 | # %m => shortname host 17 | # %(?..) => prompt conditional - %(condition.true.false) 18 | # terminal codes: 19 | # \e7 => save cursor position 20 | # \e[2A => move cursor 2 lines up 21 | # \e[1G => go to position 1 in terminal 22 | # \e8 => restore cursor position 23 | # \e[K => clears everything after the cursor on the current line 24 | # \e[2K => clear everything on the current line 25 | 26 | 27 | # turns seconds into human readable time 28 | # 165392 => 1d 21h 56m 32s 29 | # https://github.com/sindresorhus/pretty-time-zsh 30 | prompt_pure_human_time_to_var() { 31 | local human total_seconds=$1 var=$2 32 | local days=$(( total_seconds / 60 / 60 / 24 )) 33 | local hours=$(( total_seconds / 60 / 60 % 24 )) 34 | local minutes=$(( total_seconds / 60 % 60 )) 35 | local seconds=$(( total_seconds % 60 )) 36 | (( days > 0 )) && human+="${days}d " 37 | (( hours > 0 )) && human+="${hours}h " 38 | (( minutes > 0 )) && human+="${minutes}m " 39 | human+="${seconds}s" 40 | 41 | # store human readable time in variable as specified by caller 42 | typeset -g "${var}"="${human}" 43 | } 44 | 45 | # stores (into prompt_pure_cmd_exec_time) the exec time of the last command if set threshold was exceeded 46 | prompt_pure_check_cmd_exec_time() { 47 | integer elapsed 48 | (( elapsed = EPOCHSECONDS - ${prompt_pure_cmd_timestamp:-$EPOCHSECONDS} )) 49 | typeset -g prompt_pure_cmd_exec_time= 50 | (( elapsed > ${PURE_CMD_MAX_EXEC_TIME:-5} )) && { 51 | prompt_pure_human_time_to_var $elapsed "prompt_pure_cmd_exec_time" 52 | } 53 | } 54 | 55 | prompt_pure_set_title() { 56 | setopt localoptions noshwordsplit 57 | 58 | # emacs terminal does not support settings the title 59 | (( ${+EMACS} )) && return 60 | 61 | case $TTY in 62 | # Don't set title over serial console. 63 | /dev/ttyS[0-9]*) return;; 64 | esac 65 | 66 | # Show hostname if connected via ssh. 67 | local hostname= 68 | if [[ -n $prompt_pure_state[username] ]]; then 69 | # Expand in-place in case ignore-escape is used. 70 | hostname="${(%):-(%m) }" 71 | fi 72 | 73 | local -a opts 74 | case $1 in 75 | expand-prompt) opts=(-P);; 76 | ignore-escape) opts=(-r);; 77 | esac 78 | 79 | # Set title atomically in one print statement so that it works 80 | # when XTRACE is enabled. 81 | print -n $opts $'\e]0;'${hostname}${2}$'\a' 82 | } 83 | 84 | prompt_pure_preexec() { 85 | if [[ -n $prompt_pure_git_fetch_pattern ]]; then 86 | # detect when git is performing pull/fetch (including git aliases). 87 | local -H MATCH MBEGIN MEND match mbegin mend 88 | if [[ $2 =~ (git|hub)\ (.*\ )?($prompt_pure_git_fetch_pattern)(\ .*)?$ ]]; then 89 | # we must flush the async jobs to cancel our git fetch in order 90 | # to avoid conflicts with the user issued pull / fetch. 91 | async_flush_jobs 'prompt_pure' 92 | fi 93 | fi 94 | 95 | typeset -g prompt_pure_cmd_timestamp=$EPOCHSECONDS 96 | 97 | # shows the current dir and executed command in the title while a process is active 98 | prompt_pure_set_title 'ignore-escape' "$PWD:t: $2" 99 | 100 | # Disallow python virtualenv from updating the prompt, set it to 12 if 101 | # untouched by the user to indicate that Pure modified it. Here we use 102 | # magic number 12, same as in psvar. 103 | export VIRTUAL_ENV_DISABLE_PROMPT=${VIRTUAL_ENV_DISABLE_PROMPT:-12} 104 | } 105 | 106 | prompt_pure_preprompt_render() { 107 | setopt localoptions noshwordsplit 108 | 109 | # Set color for git branch/dirty status, change color if dirty checking has 110 | # been delayed. 111 | local git_color=242 112 | [[ -n ${prompt_pure_git_last_dirty_check_timestamp+x} ]] && git_color=red 113 | 114 | # Initialize the preprompt array. 115 | local -a preprompt_parts 116 | 117 | # Set the path. 118 | preprompt_parts+=('%F{blue}%~%f') 119 | 120 | # Add git branch and dirty status info. 121 | typeset -gA prompt_pure_vcs_info 122 | if [[ -n $prompt_pure_vcs_info[branch] ]]; then 123 | preprompt_parts+=("%F{$git_color}"'${prompt_pure_vcs_info[branch]}${prompt_pure_git_dirty}%f') 124 | fi 125 | # Git pull/push arrows. 126 | if [[ -n $prompt_pure_git_arrows ]]; then 127 | preprompt_parts+=('%F{cyan}${prompt_pure_git_arrows}%f') 128 | fi 129 | 130 | # Username and machine, if applicable. 131 | [[ -n $prompt_pure_state[username] ]] && preprompt_parts+=('${prompt_pure_state[username]}') 132 | # Execution time. 133 | [[ -n $prompt_pure_cmd_exec_time ]] && preprompt_parts+=('%F{yellow}${prompt_pure_cmd_exec_time}%f') 134 | 135 | local cleaned_ps1=$PROMPT 136 | local -H MATCH MBEGIN MEND 137 | if [[ $PROMPT = *$prompt_newline* ]]; then 138 | # Remove everything from the prompt until the newline. This 139 | # removes the preprompt and only the original PROMPT remains. 140 | cleaned_ps1=${PROMPT##*${prompt_newline}} 141 | fi 142 | unset MATCH MBEGIN MEND 143 | 144 | # Construct the new prompt with a clean preprompt. 145 | local -ah ps1 146 | ps1=( 147 | ${(j. .)preprompt_parts} # Join parts, space separated. 148 | $prompt_newline # Separate preprompt and prompt. 149 | $cleaned_ps1 150 | ) 151 | 152 | PROMPT="${(j..)ps1}" 153 | 154 | # Expand the prompt for future comparision. 155 | local expanded_prompt 156 | expanded_prompt="${(S%%)PROMPT}" 157 | 158 | if [[ $1 == precmd ]]; then 159 | # Initial newline, for spaciousness. 160 | print 161 | elif [[ $prompt_pure_last_prompt != $expanded_prompt ]]; then 162 | # Redraw the prompt. 163 | zle && zle .reset-prompt 164 | fi 165 | 166 | typeset -g prompt_pure_last_prompt=$expanded_prompt 167 | } 168 | 169 | prompt_pure_precmd() { 170 | # check exec time and store it in a variable 171 | prompt_pure_check_cmd_exec_time 172 | unset prompt_pure_cmd_timestamp 173 | 174 | # shows the full path in the title 175 | prompt_pure_set_title 'expand-prompt' '%~' 176 | 177 | # preform async git dirty check and fetch 178 | prompt_pure_async_tasks 179 | 180 | # Check if we should display the virtual env, we use a sufficiently high 181 | # index of psvar (12) here to avoid collisions with user defined entries. 182 | psvar[12]= 183 | # When VIRTUAL_ENV_DISABLE_PROMPT is empty, it was unset by the user and 184 | # Pure should take back control. 185 | if [[ -n $VIRTUAL_ENV ]] && [[ -z $VIRTUAL_ENV_DISABLE_PROMPT || $VIRTUAL_ENV_DISABLE_PROMPT = 12 ]]; then 186 | psvar[12]="${VIRTUAL_ENV:t}" 187 | export VIRTUAL_ENV_DISABLE_PROMPT=12 188 | fi 189 | 190 | # Make sure VIM prompt is reset. 191 | prompt_pure_reset_prompt_symbol 192 | 193 | # print the preprompt 194 | prompt_pure_preprompt_render "precmd" 195 | 196 | if [[ -n $ZSH_THEME ]]; then 197 | print "WARNING: Oh My Zsh themes are enabled (ZSH_THEME='${ZSH_THEME}'). Pure might not be working correctly." 198 | print "For more information, see: https://github.com/sindresorhus/pure#oh-my-zsh" 199 | unset ZSH_THEME # Only show this warning once. 200 | fi 201 | } 202 | 203 | prompt_pure_async_git_aliases() { 204 | setopt localoptions noshwordsplit 205 | local -a gitalias pullalias 206 | 207 | # list all aliases and split on newline. 208 | gitalias=(${(@f)"$(command git config --get-regexp "^alias\.")"}) 209 | for line in $gitalias; do 210 | parts=(${(@)=line}) # split line on spaces 211 | aliasname=${parts[1]#alias.} # grab the name (alias.[name]) 212 | shift parts # remove aliasname 213 | 214 | # check alias for pull or fetch (must be exact match). 215 | if [[ $parts =~ ^(.*\ )?(pull|fetch)(\ .*)?$ ]]; then 216 | pullalias+=($aliasname) 217 | fi 218 | done 219 | 220 | print -- ${(j:|:)pullalias} # join on pipe (for use in regex). 221 | } 222 | 223 | prompt_pure_async_vcs_info() { 224 | setopt localoptions noshwordsplit 225 | 226 | # configure vcs_info inside async task, this frees up vcs_info 227 | # to be used or configured as the user pleases. 228 | zstyle ':vcs_info:*' enable git 229 | zstyle ':vcs_info:*' use-simple true 230 | # only export two msg variables from vcs_info 231 | zstyle ':vcs_info:*' max-exports 2 232 | # export branch (%b) and git toplevel (%R) 233 | zstyle ':vcs_info:git*' formats '%b' '%R' 234 | zstyle ':vcs_info:git*' actionformats '%b|%a' '%R' 235 | 236 | vcs_info 237 | 238 | local -A info 239 | info[pwd]=$PWD 240 | info[top]=$vcs_info_msg_1_ 241 | info[branch]=$vcs_info_msg_0_ 242 | 243 | print -r - ${(@kvq)info} 244 | } 245 | 246 | # fastest possible way to check if repo is dirty 247 | prompt_pure_async_git_dirty() { 248 | setopt localoptions noshwordsplit 249 | local untracked_dirty=$1 250 | 251 | if [[ $untracked_dirty = 0 ]]; then 252 | command git diff --no-ext-diff --quiet --exit-code 253 | else 254 | test -z "$(command git status --porcelain --ignore-submodules -unormal)" 255 | fi 256 | 257 | return $? 258 | } 259 | 260 | prompt_pure_async_git_fetch() { 261 | setopt localoptions noshwordsplit 262 | 263 | # set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+) 264 | export GIT_TERMINAL_PROMPT=0 265 | # set ssh BachMode to disable all interactive ssh password prompting 266 | export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-"ssh"} -o BatchMode=yes" 267 | 268 | # Default return code, indicates Git fetch failure. 269 | local fail_code=99 270 | 271 | # Guard against all forms of password prompts. By setting the shell into 272 | # MONITOR mode we can notice when a child process prompts for user input 273 | # because it will be suspended. Since we are inside an async worker, we 274 | # have no way of transmitting the password and the only option is to 275 | # kill it. If we don't do it this way, the process will corrupt with the 276 | # async worker. 277 | setopt localtraps monitor 278 | 279 | # Make sure local HUP trap is unset to allow for signal propagation when 280 | # the async worker is flushed. 281 | trap - HUP 282 | 283 | trap ' 284 | # Unset trap to prevent infinite loop 285 | trap - CHLD 286 | if [[ $jobstates = suspended* ]]; then 287 | # Set fail code to password prompt and kill the fetch. 288 | fail_code=98 289 | kill %% 290 | fi 291 | ' CHLD 292 | 293 | command git -c gc.auto=0 fetch >/dev/null & 294 | wait $! || return $fail_code 295 | 296 | unsetopt monitor 297 | 298 | # check arrow status after a successful git fetch 299 | prompt_pure_async_git_arrows 300 | } 301 | 302 | prompt_pure_async_git_arrows() { 303 | setopt localoptions noshwordsplit 304 | command git rev-list --left-right --count HEAD...@'{u}' 305 | } 306 | 307 | prompt_pure_async_tasks() { 308 | setopt localoptions noshwordsplit 309 | 310 | # initialize async worker 311 | ((!${prompt_pure_async_init:-0})) && { 312 | async_start_worker "prompt_pure" -u -n 313 | async_register_callback "prompt_pure" prompt_pure_async_callback 314 | typeset -g prompt_pure_async_init=1 315 | } 316 | 317 | # Update the current working directory of the async worker. 318 | async_worker_eval "prompt_pure" builtin cd -q $PWD 319 | 320 | typeset -gA prompt_pure_vcs_info 321 | 322 | local -H MATCH MBEGIN MEND 323 | if [[ $PWD != ${prompt_pure_vcs_info[pwd]}* ]]; then 324 | # stop any running async jobs 325 | async_flush_jobs "prompt_pure" 326 | 327 | # reset git preprompt variables, switching working tree 328 | unset prompt_pure_git_dirty 329 | unset prompt_pure_git_last_dirty_check_timestamp 330 | unset prompt_pure_git_arrows 331 | unset prompt_pure_git_fetch_pattern 332 | prompt_pure_vcs_info[branch]= 333 | prompt_pure_vcs_info[top]= 334 | fi 335 | unset MATCH MBEGIN MEND 336 | 337 | async_job "prompt_pure" prompt_pure_async_vcs_info 338 | 339 | # # only perform tasks inside git working tree 340 | [[ -n $prompt_pure_vcs_info[top] ]] || return 341 | 342 | prompt_pure_async_refresh 343 | } 344 | 345 | prompt_pure_async_refresh() { 346 | setopt localoptions noshwordsplit 347 | 348 | if [[ -z $prompt_pure_git_fetch_pattern ]]; then 349 | # we set the pattern here to avoid redoing the pattern check until the 350 | # working three has changed. pull and fetch are always valid patterns. 351 | typeset -g prompt_pure_git_fetch_pattern="pull|fetch" 352 | async_job "prompt_pure" prompt_pure_async_git_aliases 353 | fi 354 | 355 | async_job "prompt_pure" prompt_pure_async_git_arrows 356 | 357 | # do not preform git fetch if it is disabled or in home folder. 358 | if (( ${PURE_GIT_PULL:-1} )) && [[ $prompt_pure_vcs_info[top] != $HOME ]]; then 359 | # tell worker to do a git fetch 360 | async_job "prompt_pure" prompt_pure_async_git_fetch 361 | fi 362 | 363 | # if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout 364 | integer time_since_last_dirty_check=$(( EPOCHSECONDS - ${prompt_pure_git_last_dirty_check_timestamp:-0} )) 365 | if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then 366 | unset prompt_pure_git_last_dirty_check_timestamp 367 | # check check if there is anything to pull 368 | async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} 369 | fi 370 | } 371 | 372 | prompt_pure_check_git_arrows() { 373 | setopt localoptions noshwordsplit 374 | local arrows left=${1:-0} right=${2:-0} 375 | 376 | (( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-⇣} 377 | (( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-⇡} 378 | 379 | [[ -n $arrows ]] || return 380 | typeset -g REPLY=$arrows 381 | } 382 | 383 | prompt_pure_async_callback() { 384 | setopt localoptions noshwordsplit 385 | local job=$1 code=$2 output=$3 exec_time=$4 next_pending=$6 386 | local do_render=0 387 | 388 | case $job in 389 | prompt_pure_async_vcs_info) 390 | local -A info 391 | typeset -gA prompt_pure_vcs_info 392 | 393 | # parse output (z) and unquote as array (Q@) 394 | info=("${(Q@)${(z)output}}") 395 | local -H MATCH MBEGIN MEND 396 | if [[ $info[pwd] != $PWD ]]; then 397 | # The path has changed since the check started, abort. 398 | return 399 | fi 400 | # check if git toplevel has changed 401 | if [[ $info[top] = $prompt_pure_vcs_info[top] ]]; then 402 | # if stored pwd is part of $PWD, $PWD is shorter and likelier 403 | # to be toplevel, so we update pwd 404 | if [[ $prompt_pure_vcs_info[pwd] = ${PWD}* ]]; then 405 | prompt_pure_vcs_info[pwd]=$PWD 406 | fi 407 | else 408 | # store $PWD to detect if we (maybe) left the git path 409 | prompt_pure_vcs_info[pwd]=$PWD 410 | fi 411 | unset MATCH MBEGIN MEND 412 | 413 | # update has a git toplevel set which means we just entered a new 414 | # git directory, run the async refresh tasks 415 | [[ -n $info[top] ]] && [[ -z $prompt_pure_vcs_info[top] ]] && prompt_pure_async_refresh 416 | 417 | # always update branch and toplevel 418 | prompt_pure_vcs_info[branch]=$info[branch] 419 | prompt_pure_vcs_info[top]=$info[top] 420 | 421 | do_render=1 422 | ;; 423 | prompt_pure_async_git_aliases) 424 | if [[ -n $output ]]; then 425 | # append custom git aliases to the predefined ones. 426 | prompt_pure_git_fetch_pattern+="|$output" 427 | fi 428 | ;; 429 | prompt_pure_async_git_dirty) 430 | local prev_dirty=$prompt_pure_git_dirty 431 | if (( code == 0 )); then 432 | unset prompt_pure_git_dirty 433 | else 434 | typeset -g prompt_pure_git_dirty="*" 435 | fi 436 | 437 | [[ $prev_dirty != $prompt_pure_git_dirty ]] && do_render=1 438 | 439 | # When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color. 440 | # To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this 441 | # variable. Thus, only upon next rendering of the preprompt will the result appear in a different color. 442 | (( $exec_time > 5 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS 443 | ;; 444 | prompt_pure_async_git_fetch|prompt_pure_async_git_arrows) 445 | # prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows 446 | # after a successful fetch. 447 | case $code in 448 | 0) 449 | local REPLY 450 | prompt_pure_check_git_arrows ${(ps:\t:)output} 451 | if [[ $prompt_pure_git_arrows != $REPLY ]]; then 452 | typeset -g prompt_pure_git_arrows=$REPLY 453 | do_render=1 454 | fi 455 | ;; 456 | 99|98) 457 | # Git fetch failed. 458 | ;; 459 | *) 460 | # Non-zero exit status from prompt_pure_async_git_arrows, 461 | # indicating that there is no upstream configured. 462 | if [[ -n $prompt_pure_git_arrows ]]; then 463 | unset prompt_pure_git_arrows 464 | do_render=1 465 | fi 466 | ;; 467 | esac 468 | ;; 469 | esac 470 | 471 | if (( next_pending )); then 472 | (( do_render )) && typeset -g prompt_pure_async_render_requested=1 473 | return 474 | fi 475 | 476 | [[ ${prompt_pure_async_render_requested:-$do_render} = 1 ]] && prompt_pure_preprompt_render 477 | unset prompt_pure_async_render_requested 478 | } 479 | 480 | prompt_pure_reset_prompt_symbol() { 481 | prompt_pure_state[prompt]=${PURE_PROMPT_SYMBOL:-❯} 482 | } 483 | 484 | prompt_pure_update_vim_prompt_widget() { 485 | setopt localoptions noshwordsplit 486 | prompt_pure_state[prompt]=${${KEYMAP/vicmd/${PURE_PROMPT_VICMD_SYMBOL:-❮}}/(main|viins)/${PURE_PROMPT_SYMBOL:-❯}} 487 | zle && zle .reset-prompt 488 | } 489 | 490 | prompt_pure_reset_vim_prompt_widget() { 491 | setopt localoptions noshwordsplit 492 | prompt_pure_reset_prompt_symbol 493 | zle && zle .reset-prompt 494 | } 495 | 496 | prompt_pure_state_setup() { 497 | setopt localoptions noshwordsplit 498 | 499 | # Check SSH_CONNECTION and the current state. 500 | local ssh_connection=${SSH_CONNECTION:-$PROMPT_PURE_SSH_CONNECTION} 501 | local username 502 | if [[ -z $ssh_connection ]] && (( $+commands[who] )); then 503 | # When changing user on a remote system, the $SSH_CONNECTION 504 | # environment variable can be lost, attempt detection via who. 505 | local who_out 506 | who_out=$(who -m 2>/dev/null) 507 | if (( $? )); then 508 | # Who am I not supported, fallback to plain who. 509 | who_out=$(who 2>/dev/null | grep ${TTY#/dev/}) 510 | fi 511 | 512 | local reIPv6='(([0-9a-fA-F]+:)|:){2,}[0-9a-fA-F]+' # Simplified, only checks partial pattern. 513 | local reIPv4='([0-9]{1,3}\.){3}[0-9]+' # Simplified, allows invalid ranges. 514 | # Here we assume two non-consecutive periods represents a 515 | # hostname. This matches foo.bar.baz, but not foo.bar. 516 | local reHostname='([.][^. ]+){2}' 517 | 518 | # Usually the remote address is surrounded by parenthesis, but 519 | # not on all systems (e.g. busybox). 520 | local -H MATCH MBEGIN MEND 521 | if [[ $who_out =~ "\(?($reIPv4|$reIPv6|$reHostname)\)?\$" ]]; then 522 | ssh_connection=$MATCH 523 | 524 | # Export variable to allow detection propagation inside 525 | # shells spawned by this one (e.g. tmux does not always 526 | # inherit the same tty, which breaks detection). 527 | export PROMPT_PURE_SSH_CONNECTION=$ssh_connection 528 | fi 529 | unset MATCH MBEGIN MEND 530 | fi 531 | 532 | # show username@host if logged in through SSH 533 | [[ -n $ssh_connection ]] && username='%F{242}%n@%m%f' 534 | 535 | # show username@host if root, with username in white 536 | [[ $UID -eq 0 ]] && username='%F{white}%n%f%F{242}@%m%f' 537 | 538 | typeset -gA prompt_pure_state 539 | prompt_pure_state=( 540 | username "$username" 541 | prompt "${PURE_PROMPT_SYMBOL:-❯}" 542 | ) 543 | } 544 | 545 | prompt_pure_setup() { 546 | # Prevent percentage showing up if output doesn't end with a newline. 547 | export PROMPT_EOL_MARK='' 548 | 549 | prompt_opts=(subst percent) 550 | 551 | # borrowed from promptinit, sets the prompt options in case pure was not 552 | # initialized via promptinit. 553 | setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}" 554 | 555 | if [[ -z $prompt_newline ]]; then 556 | # This variable needs to be set, usually set by promptinit. 557 | typeset -g prompt_newline=$'\n%{\r%}' 558 | fi 559 | 560 | zmodload zsh/datetime 561 | zmodload zsh/zle 562 | zmodload zsh/parameter 563 | 564 | autoload -Uz add-zsh-hook 565 | autoload -Uz vcs_info 566 | autoload -Uz async && async 567 | 568 | # The add-zle-hook-widget function is not guaranteed 569 | # to be available, it was added in Zsh 5.3. 570 | autoload -Uz +X add-zle-hook-widget 2>/dev/null 571 | 572 | add-zsh-hook precmd prompt_pure_precmd 573 | add-zsh-hook preexec prompt_pure_preexec 574 | 575 | prompt_pure_state_setup 576 | 577 | zle -N prompt_pure_update_vim_prompt_widget 578 | zle -N prompt_pure_reset_vim_prompt_widget 579 | if (( $+functions[add-zle-hook-widget] )); then 580 | add-zle-hook-widget zle-line-finish prompt_pure_reset_vim_prompt_widget 581 | add-zle-hook-widget zle-keymap-select prompt_pure_update_vim_prompt_widget 582 | fi 583 | 584 | # if a virtualenv is activated, display it in grey 585 | PROMPT='%(12V.%F{242}%12v%f .)' 586 | 587 | # prompt turns red if the previous command didn't exit with 0 588 | PROMPT+='%(?.%F{magenta}.%F{red})${prompt_pure_state[prompt]}%f ' 589 | 590 | # Store prompt expansion symbols for in-place expansion via (%). For 591 | # some reason it does not work without storing them in a variable first. 592 | typeset -ga prompt_pure_debug_depth 593 | prompt_pure_debug_depth=('%e' '%N' '%x') 594 | 595 | # Compare is used to check if %N equals %x. When they differ, the main 596 | # prompt is used to allow displaying both file name and function. When 597 | # they match, we use the secondary prompt to avoid displaying duplicate 598 | # information. 599 | local -A ps4_parts 600 | ps4_parts=( 601 | depth '%F{yellow}${(l:${(%)prompt_pure_debug_depth[1]}::+:)}%f' 602 | compare '${${(%)prompt_pure_debug_depth[2]}:#${(%)prompt_pure_debug_depth[3]}}' 603 | main '%F{blue}${${(%)prompt_pure_debug_depth[3]}:t}%f%F{242}:%I%f %F{242}@%f%F{blue}%N%f%F{242}:%i%f' 604 | secondary '%F{blue}%N%f%F{242}:%i' 605 | prompt '%F{242}>%f ' 606 | ) 607 | # Combine the parts with conditional logic. First the `:+` operator is 608 | # used to replace `compare` either with `main` or an ampty string. Then 609 | # the `:-` operator is used so that if `compare` becomes an empty 610 | # string, it is replaced with `secondary`. 611 | local ps4_symbols='${${'${ps4_parts[compare]}':+"'${ps4_parts[main]}'"}:-"'${ps4_parts[secondary]}'"}' 612 | 613 | # Improve the debug prompt (PS4), show depth by repeating the +-sign and 614 | # add colors to highlight essential parts like file and function name. 615 | PROMPT4="${ps4_parts[depth]} ${ps4_symbols}${ps4_parts[prompt]}" 616 | 617 | unset ZSH_THEME # Guard against Oh My Zsh themes overriding Pure. 618 | } 619 | 620 | prompt_pure_setup "$@" 621 | -------------------------------------------------------------------------------- /prompts/prompt_purer_setup: -------------------------------------------------------------------------------- 1 | # Pure 2 | # by Sindre Sorhus 3 | # https://github.com/sindresorhus/pure 4 | # MIT License 5 | 6 | # For my own and others sanity 7 | # git: 8 | # %b => current branch 9 | # %a => current action (rebase/merge) 10 | # prompt: 11 | # %F => color dict 12 | # %f => reset color 13 | # %~ => current path 14 | # %* => time 15 | # %n => username 16 | # %m => shortname host 17 | # %(?..) => prompt conditional - %(condition.true.false) 18 | # terminal codes: 19 | # \e7 => save cursor position 20 | # \e[2A => move cursor 2 lines up 21 | # \e[1G => go to position 1 in terminal 22 | # \e8 => restore cursor position 23 | # \e[K => clears everything after the cursor on the current line 24 | # \e[2K => clear everything on the current line 25 | 26 | PURER_PROMPT_COMMAND_COUNT=0 27 | STATUS_COLOR='cyan' 28 | 29 | # turns seconds into human readable time 30 | # 165392 => 1d 21h 56m 32s 31 | # https://github.com/sindresorhus/pretty-time-zsh 32 | prompt_pure_human_time_to_var() { 33 | local human=" [" total_seconds=$1 var=$2 34 | local days=$(( total_seconds / 60 / 60 / 24 )) 35 | local hours=$(( total_seconds / 60 / 60 % 24 )) 36 | local minutes=$(( total_seconds / 60 % 60 )) 37 | local seconds=$(( total_seconds % 60 )) 38 | (( days > 0 )) && human+="${days}d " 39 | (( hours > 0 )) && human+="${hours}h " 40 | (( minutes > 0 )) && human+="${minutes}m " 41 | human+="${seconds}s]" 42 | 43 | # store human readable time in variable as specified by caller 44 | typeset -g "${var}"="${human}" 45 | } 46 | 47 | # stores (into prompt_pure_cmd_exec_time) the exec time of the last command if set threshold was exceeded 48 | prompt_pure_check_cmd_exec_time() { 49 | integer elapsed 50 | (( elapsed = EPOCHSECONDS - ${prompt_pure_cmd_timestamp:-$EPOCHSECONDS} )) 51 | prompt_pure_cmd_exec_time= 52 | (( elapsed > ${PURE_CMD_MAX_EXEC_TIME:=5} )) && { 53 | prompt_pure_human_time_to_var $elapsed "prompt_pure_cmd_exec_time" 54 | } 55 | } 56 | 57 | prompt_pure_clear_screen() { 58 | # enable output to terminal 59 | zle -I 60 | # clear screen and move cursor to (0, 0) 61 | print -n '\e[2J\e[0;0H' 62 | # reset command count to zero so we don't start with a blank line 63 | PURER_PROMPT_COMMAND_COUNT=0 64 | # print preprompt 65 | prompt_pure_preprompt_render precmd 66 | } 67 | 68 | # set STATUS_COLOR: cyan for "insert", green for "normal" mode. 69 | prompt_purer_vim_mode() { 70 | STATUS_COLOR="${${KEYMAP/vicmd/green}/(main|viins)/cyan}" 71 | prompt_pure_preprompt_render 72 | } 73 | 74 | prompt_pure_set_title() { 75 | # emacs terminal does not support settings the title 76 | (( ${+EMACS} )) && return 77 | 78 | # tell the terminal we are setting the title 79 | print -n '\e]0;' 80 | # show hostname if connected through ssh 81 | [[ -n $SSH_CONNECTION ]] && print -Pn '(%m) ' 82 | case $1 in 83 | expand-prompt) 84 | print -Pn $2;; 85 | ignore-escape) 86 | print -rn $2;; 87 | esac 88 | # end set title 89 | print -n '\a' 90 | } 91 | 92 | prompt_pure_preexec() { 93 | # attempt to detect and prevent prompt_pure_async_git_fetch from interfering with user initiated git or hub fetch 94 | [[ $2 =~ (git|hub)\ .*(pull|fetch) ]] && async_flush_jobs 'prompt_pure' 95 | 96 | prompt_pure_cmd_timestamp=$EPOCHSECONDS 97 | 98 | # shows the current dir and executed command in the title while a process is active 99 | prompt_pure_set_title 'ignore-escape' "$PWD:t: $2" 100 | 101 | # Disallow python virtualenv from updating the prompt, set it to 12 if 102 | # untouched by the user to indicate that Pure modified it. Here we use 103 | # magic number 12, same as in psvar. 104 | export VIRTUAL_ENV_DISABLE_PROMPT=${VIRTUAL_ENV_DISABLE_PROMPT:-12} 105 | } 106 | 107 | # string length ignoring ansi escapes 108 | prompt_pure_string_length_to_var() { 109 | local str=$1 var=$2 length 110 | # perform expansion on str and check length 111 | length=$(( ${#${(S%%)str//(\%([KF1]|)\{*\}|\%[Bbkf])}} )) 112 | 113 | # store string length in variable as specified by caller 114 | typeset -g "${var}"="${length}" 115 | } 116 | 117 | prompt_pure_preprompt_render() { 118 | # store the current prompt_subst setting so that it can be restored later 119 | local prompt_subst_status=$options[prompt_subst] 120 | 121 | # make sure prompt_subst is unset to prevent parameter expansion in preprompt 122 | setopt local_options no_prompt_subst 123 | 124 | # check that no command is currently running, the preprompt will otherwise be rendered in the wrong place 125 | [[ -n ${prompt_pure_cmd_timestamp+x} && "$1" != "precmd" ]] && return 126 | 127 | # set color for git branch/dirty status, change color if dirty checking has been delayed 128 | local git_color=242 129 | [[ -n ${prompt_pure_git_last_dirty_check_timestamp+x} ]] && git_color=red 130 | 131 | # construct preprompt 132 | local preprompt="" 133 | 134 | 135 | # add a newline between commands 136 | FIRST_COMMAND_THRESHOLD=1 137 | if [[ "$PURER_PROMPT_COMMAND_COUNT" -gt "$FIRST_COMMAND_THRESHOLD" ]]; then 138 | preprompt+=$'\n' 139 | fi 140 | 141 | local symbol_color="%(?.${PURE_PROMPT_SYMBOL_COLOR:-magenta}.red)" 142 | 143 | # show virtual env 144 | preprompt+="%(12V.%F{242}%12v%f .)" 145 | # begin with symbol, colored by previous command exit code 146 | preprompt+="%F{$symbol_color}${PURE_PROMPT_SYMBOL:-❯}%f " 147 | # directory, colored by vim status 148 | preprompt+="%B%F{$STATUS_COLOR}%c%f%b" 149 | # git info 150 | preprompt+="%F{$git_color}${vcs_info_msg_0_}${prompt_pure_git_dirty}%f" 151 | # git pull/push arrows 152 | preprompt+="%F{cyan}${prompt_pure_git_arrows}%f" 153 | # username and machine if applicable 154 | preprompt+=$prompt_pure_username 155 | # execution time 156 | preprompt+="%B%F{242}${prompt_pure_cmd_exec_time}%f%b" 157 | 158 | preprompt+=" " 159 | 160 | # make sure prompt_pure_last_preprompt is a global array 161 | typeset -g -a prompt_pure_last_preprompt 162 | 163 | PROMPT="$preprompt" 164 | 165 | # if executing through precmd, do not perform fancy terminal editing 166 | if [[ "$1" != "precmd" ]]; then 167 | # only redraw if the expanded preprompt has changed 168 | # [[ "${prompt_pure_last_preprompt[2]}" != "${(S%%)preprompt}" ]] || return 169 | 170 | # redraw prompt (also resets cursor position) 171 | zle && zle .reset-prompt 172 | 173 | setopt no_prompt_subst 174 | fi 175 | 176 | # store both unexpanded and expanded preprompt for comparison 177 | prompt_pure_last_preprompt=("$preprompt" "${(S%%)preprompt}") 178 | } 179 | 180 | prompt_pure_precmd() { 181 | # check exec time and store it in a variable 182 | prompt_pure_check_cmd_exec_time 183 | 184 | # by making sure that prompt_pure_cmd_timestamp is defined here the async functions are prevented from interfering 185 | # with the initial preprompt rendering 186 | prompt_pure_cmd_timestamp= 187 | 188 | # shows the full path in the title 189 | prompt_pure_set_title 'expand-prompt' '%~' 190 | 191 | # get vcs info 192 | vcs_info 193 | 194 | # preform async git dirty check and fetch 195 | prompt_pure_async_tasks 196 | 197 | # Check if we should display the virtual env, we use a sufficiently high 198 | # index of psvar (12) here to avoid collisions with user defined entries. 199 | psvar[12]= 200 | # When VIRTUAL_ENV_DISABLE_PROMPT is empty, it was unset by the user and 201 | # Pure should take back control. 202 | if [[ -n $VIRTUAL_ENV ]] && [[ -z $VIRTUAL_ENV_DISABLE_PROMPT || $VIRTUAL_ENV_DISABLE_PROMPT = 12 ]]; then 203 | psvar[12]="${VIRTUAL_ENV:t}" 204 | export VIRTUAL_ENV_DISABLE_PROMPT=12 205 | fi 206 | 207 | # print the preprompt 208 | prompt_pure_preprompt_render "precmd" 209 | 210 | # Increment command counter 211 | PURER_PROMPT_COMMAND_COUNT=$((PURER_PROMPT_COMMAND_COUNT+1)) 212 | 213 | # print the preprompt 214 | prompt_pure_preprompt_render "precmd" 215 | 216 | # remove the prompt_pure_cmd_timestamp, indicating that precmd has completed 217 | unset prompt_pure_cmd_timestamp 218 | } 219 | 220 | # fastest possible way to check if repo is dirty 221 | prompt_pure_async_git_dirty() { 222 | setopt localoptions noshwordsplit 223 | local untracked_dirty=$1 dir=$2 224 | 225 | # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks 226 | builtin cd -q $dir 227 | 228 | if [[ $untracked_dirty = 0 ]]; then 229 | command git diff --no-ext-diff --quiet --exit-code 230 | else 231 | test -z "$(command git status --porcelain --ignore-submodules -unormal)" 232 | fi 233 | 234 | return $? 235 | } 236 | 237 | prompt_pure_async_git_fetch() { 238 | setopt localoptions noshwordsplit 239 | # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks 240 | builtin cd -q $1 241 | 242 | # set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+) 243 | export GIT_TERMINAL_PROMPT=0 244 | # set ssh BachMode to disable all interactive ssh password prompting 245 | export GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-"ssh -o BatchMode=yes"} 246 | 247 | command git -c gc.auto=0 fetch &>/dev/null || return 1 248 | 249 | # check arrow status after a successful git fetch 250 | prompt_pure_async_git_arrows $1 251 | } 252 | 253 | prompt_pure_async_git_arrows() { 254 | setopt localoptions noshwordsplit 255 | builtin cd -q $1 256 | command git rev-list --left-right --count HEAD...@'{u}' 257 | } 258 | 259 | prompt_pure_async_tasks() { 260 | setopt localoptions noshwordsplit 261 | 262 | # initialize async worker 263 | ((!${prompt_pure_async_init:-0})) && { 264 | async_start_worker "prompt_pure" -u -n 265 | async_register_callback "prompt_pure" prompt_pure_async_callback 266 | prompt_pure_async_init=1 267 | } 268 | 269 | # store working_tree without the "x" prefix 270 | local working_tree="${vcs_info_msg_1_#x}" 271 | 272 | # check if the working tree changed (prompt_pure_current_working_tree is prefixed by "x") 273 | if [[ ${prompt_pure_current_working_tree#x} != $working_tree ]]; then 274 | # stop any running async jobs 275 | async_flush_jobs "prompt_pure" 276 | 277 | # reset git preprompt variables, switching working tree 278 | unset prompt_pure_git_dirty 279 | unset prompt_pure_git_last_dirty_check_timestamp 280 | prompt_pure_git_arrows= 281 | 282 | # set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS 283 | prompt_pure_current_working_tree="x${working_tree}" 284 | fi 285 | 286 | # only perform tasks inside git working tree 287 | [[ -n $working_tree ]] || return 288 | 289 | async_job "prompt_pure" prompt_pure_async_git_arrows $working_tree 290 | 291 | # do not preform git fetch if it is disabled or working_tree == HOME 292 | if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then 293 | # tell worker to do a git fetch 294 | async_job "prompt_pure" prompt_pure_async_git_fetch $working_tree 295 | fi 296 | 297 | # if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout 298 | integer time_since_last_dirty_check=$(( EPOCHSECONDS - ${prompt_pure_git_last_dirty_check_timestamp:-0} )) 299 | if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then 300 | unset prompt_pure_git_last_dirty_check_timestamp 301 | # check check if there is anything to pull 302 | async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} $working_tree 303 | fi 304 | } 305 | 306 | prompt_pure_check_git_arrows() { 307 | setopt localoptions noshwordsplit 308 | local arrows left=${1:-0} right=${2:-0} 309 | 310 | (( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-⇣} 311 | (( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-⇡} 312 | 313 | [[ -n $arrows ]] || return 314 | typeset -g REPLY=" $arrows" 315 | } 316 | 317 | prompt_pure_async_callback() { 318 | setopt localoptions noshwordsplit 319 | local job=$1 code=$2 output=$3 exec_time=$4 320 | 321 | case $job in 322 | prompt_pure_async_git_dirty) 323 | local prev_dirty=$prompt_pure_git_dirty 324 | if (( code == 0 )); then 325 | prompt_pure_git_dirty= 326 | else 327 | prompt_pure_git_dirty="*" 328 | fi 329 | 330 | [[ $prev_dirty != $prompt_pure_git_dirty ]] && prompt_pure_preprompt_render 331 | 332 | # When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color. 333 | # To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this 334 | # variable. Thus, only upon next rendering of the preprompt will the result appear in a different color. 335 | (( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS 336 | ;; 337 | prompt_pure_async_git_fetch|prompt_pure_async_git_arrows) 338 | # prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows 339 | # after a successful fetch. 340 | if (( code == 0 )); then 341 | local REPLY 342 | prompt_pure_check_git_arrows ${(ps:\t:)output} 343 | if [[ $prompt_pure_git_arrows != $REPLY ]]; then 344 | prompt_pure_git_arrows=$REPLY 345 | prompt_pure_preprompt_render 346 | fi 347 | fi 348 | ;; 349 | esac 350 | } 351 | 352 | prompt_pure_setup() { 353 | # prevent percentage showing up 354 | # if output doesn't end with a newline 355 | export PROMPT_EOL_MARK='' 356 | 357 | # prompt_opts=(subst percent) 358 | 359 | # borrowed from promptinit, sets the prompt options in case pure was not 360 | # initialized via promptinit. 361 | # setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}" 362 | 363 | zmodload zsh/datetime 364 | zmodload zsh/zle 365 | zmodload zsh/parameter 366 | 367 | autoload -Uz add-zsh-hook 368 | autoload -Uz vcs_info 369 | autoload -Uz async && async 370 | 371 | add-zsh-hook precmd prompt_pure_precmd 372 | add-zsh-hook preexec prompt_pure_preexec 373 | 374 | zstyle ':vcs_info:*' enable git 375 | zstyle ':vcs_info:*' use-simple true 376 | # only export two msg variables from vcs_info 377 | zstyle ':vcs_info:*' max-exports 2 378 | # vcs_info_msg_0_ = ' %b' (for branch) 379 | # vcs_info_msg_1_ = 'x%R' git top level (%R), x-prefix prevents creation of a named path (AUTO_NAME_DIRS) 380 | zstyle ':vcs_info:git*' formats ' %b' 'x%R' 381 | zstyle ':vcs_info:git*' actionformats ' %b|%a' 'x%R' 382 | 383 | # if the user has not registered a custom zle widget for clear-screen, 384 | # override the builtin one so that the preprompt is displayed correctly when 385 | # ^L is issued. 386 | if [[ $widgets[clear-screen] == 'builtin' ]]; then 387 | zle -N clear-screen prompt_pure_clear_screen 388 | fi 389 | 390 | # register custom function for vim-mode 391 | zle -N zle-line-init prompt_purer_vim_mode 392 | zle -N zle-keymap-select prompt_purer_vim_mode 393 | 394 | # show username@host if logged in through SSH 395 | [[ "$SSH_CONNECTION" != '' ]] && prompt_pure_username=' %F{242}%n@%m%f' 396 | 397 | # show username@host if root, with username in white 398 | [[ $UID -eq 0 ]] && prompt_pure_username=' %F{white}%n%f%F{242}@%m%f' 399 | 400 | # create prompt 401 | prompt_pure_preprompt_render 'precmd' 402 | } 403 | 404 | prompt_pure_setup "$@" 405 | -------------------------------------------------------------------------------- /prompts/prompt_shellder_setup: -------------------------------------------------------------------------------- 1 | # vim:ft=zsh ts=2 sw=2 sts=2 2 | 3 | # 4 | # Segment drawing 5 | # 6 | CURRENT_BG='NONE' 7 | 8 | # 9 | # color scheme 10 | # 11 | SHELLDER_CONTEXT_BG=${SHELLDER_CONTEXT_BG:-238} 12 | SHELLDER_CONTEXT_FG=${SHELLDER_CONTEXT_FG:-250} 13 | 14 | SHELLDER_DIRECTORY_BG=${SHELLDER_DIRECTORY_BG:-234} 15 | SHELLDER_DIRECTORY_FG=${SHELLDER_DIRECTORY_FG:-231} 16 | 17 | SHELLDER_GIT_CLEAN_BG=${SHELLDER_GIT_CLEAN_BG:-'green'} 18 | SHELLDER_GIT_CLEAN_FG=${SHELLDER_GIT_CLEAN_FG:-'black'} 19 | SHELLDER_GIT_DIRTY_BG=${SHELLDER_GIT_DIRTY_BG:-202} 20 | SHELLDER_GIT_DIRTY_FG=${SHELLDER_GIT_DIRTY_FG:-'black'} 21 | 22 | SHELLDER_VIRTUALENV_BG=${SHELLDER_VIRTUALENV_BG:-'blue'} 23 | SHELLDER_VIRTUALENV_FG=${SHELLDER_VIRTUALENV_FG:-'black'} 24 | 25 | SHELLDER_STATUS_BG=${SHELLDER_STATUS_BG:-'black'} 26 | SHELLDER_STATUS_FG=${SHELLDER_STATUS_FG:-'default'} 27 | 28 | # Special Powerline characters 29 | () { 30 | local LC_ALL="" LC_CTYPE="en_US.UTF-8" 31 | SEGMENT_SEPARATOR=$'\ue0b0' 32 | } 33 | 34 | # Begin a segment 35 | # Takes two arguments, background and foreground. Both can be omitted, 36 | # rendering default background/foreground. 37 | prompt_segment() { 38 | local bg fg 39 | [[ -n $1 ]] && bg="%K{$1}" || bg="%k" 40 | [[ -n $2 ]] && fg="%F{$2}" || fg="%f" 41 | if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then 42 | echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} " 43 | else 44 | echo -n "%{$bg%}%{$fg%} " 45 | fi 46 | CURRENT_BG=$1 47 | [[ -n $3 ]] && echo -n $3 48 | } 49 | 50 | # End the prompt, closing any open segments 51 | prompt_end() { 52 | if [[ -n $CURRENT_BG ]]; then 53 | echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR" 54 | else 55 | echo -n "%{%k%}" 56 | fi 57 | echo -n "%{%f%}" 58 | CURRENT_BG='' 59 | } 60 | 61 | 62 | # 63 | # Prompt functions 64 | # 65 | 66 | # Context: user@hostname (who am I and where am I) 67 | prompt_context() { 68 | local prompt 69 | if [[ "$USER" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then 70 | if [[ "$USER" != "$DEFAULT_USER" ]]; then 71 | prompt="%(!.%{%F{yellow}%}.)$USER@%m" 72 | else 73 | prompt="%(!.%{%F{yellow}%}.)%m" 74 | fi 75 | prompt_segment $SHELLDER_CONTEXT_BG $SHELLDER_CONTEXT_FG $prompt 76 | fi 77 | } 78 | 79 | # Git: branch/detached head, dirty status 80 | prompt_git() { 81 | local repo_path 82 | repo_path=$(git rev-parse --git-dir 2>/dev/null) 83 | 84 | if [[ -n $repo_path ]]; then 85 | local PL_BRANCH_CHAR dirty bgcolor fgcolor mode ref 86 | 87 | () { 88 | local LC_ALL="" LC_CTYPE="en_US.UTF-8" 89 | PL_BRANCH_CHAR=$'\ue0a0' #  90 | } 91 | 92 | dirty=$(command git status --porcelain --ignore-submodules=dirty 2> /dev/null) 93 | if [[ -n $dirty ]]; then 94 | if [[ -z $MSYS ]]; then 95 | bgcolor='yellow' 96 | fgcolor='black' 97 | else 98 | bgcolor=$SHELLDER_GIT_DIRTY_BG # vcs_info will be disabled with MSYS2, warn it with color 99 | fgcolor=$SHELLDER_GIT_DIRTY_FG 100 | fi 101 | else 102 | bgcolor=$SHELLDER_GIT_CLEAN_BG 103 | fgcolor=$SHELLDER_GIT_CLEAN_FG 104 | fi 105 | prompt_segment $bgcolor $fgcolor 106 | 107 | if [[ -e "${repo_path}/BISECT_LOG" ]]; then 108 | mode=" " 109 | elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then 110 | mode=" >M<" 111 | elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then 112 | mode=" >R>" 113 | fi 114 | 115 | # vcs_info is too slow with MSYS2 (~300ms with i7-6770K + SSD) 116 | if [[ -z $MSYS ]]; then 117 | autoload -Uz vcs_info 118 | zstyle ':vcs_info:*' enable git 119 | zstyle ':vcs_info:*' check-for-changes true 120 | zstyle ':vcs_info:*' stagedstr '✚' 121 | zstyle ':vcs_info:*' unstagedstr '●' 122 | zstyle ':vcs_info:*' formats ' %u%c' 123 | zstyle ':vcs_info:*' actionformats ' %u%c' 124 | vcs_info 125 | else 126 | if [[ -n $dirty ]]; then 127 | vcs_info_msg_0_=' !' 128 | fi 129 | fi 130 | 131 | ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(git rev-parse --short HEAD 2> /dev/null)" 132 | echo -n "${ref/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}" 133 | fi 134 | } 135 | 136 | prompt_hg() { 137 | local rev status 138 | if $(hg id >/dev/null 2>&1); then 139 | if $(hg prompt >/dev/null 2>&1); then 140 | if [[ $(hg prompt "{status|unknown}") = "?" ]]; then 141 | # if files are not added 142 | prompt_segment red white 143 | st='±' 144 | elif [[ -n $(hg prompt "{status|modified}") ]]; then 145 | # if any modification 146 | prompt_segment yellow black 147 | st='±' 148 | else 149 | # if working copy is clean 150 | prompt_segment green black 151 | fi 152 | echo -n $(hg prompt "☿ {rev}@{branch}") $st 153 | else 154 | st="" 155 | rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g') 156 | branch=$(hg id -b 2>/dev/null) 157 | if `hg st | grep -q "^\?"`; then 158 | prompt_segment red black 159 | st='±' 160 | elif `hg st | grep -q "^[MA]"`; then 161 | prompt_segment yellow black 162 | st='±' 163 | else 164 | prompt_segment green black 165 | fi 166 | echo -n "☿ $rev@$branch" $st 167 | fi 168 | fi 169 | } 170 | 171 | # Dir: current working directory 172 | prompt_dir() { 173 | local dir 174 | if (( $+functions[shrink_path] )); then 175 | dir=$(shrink_path -f) 176 | else 177 | dir='%~' 178 | fi 179 | prompt_segment $SHELLDER_DIRECTORY_BG $SHELLDER_DIRECTORY_FG $dir 180 | } 181 | 182 | # Virtualenv: current working virtualenv 183 | prompt_virtualenv() { 184 | local virtualenv_path="$VIRTUAL_ENV" 185 | if [[ -n $virtualenv_path && -n $VIRTUAL_ENV_DISABLE_PROMPT ]]; then 186 | prompt_segment $SHELLDER_VIRTUALENV_BG $SHELLDER_VIRTUALENV_FG "(`basename $virtualenv_path`)" 187 | fi 188 | } 189 | 190 | # Status: error + root + background jobs 191 | prompt_status() { 192 | local symbols 193 | symbols=() 194 | [[ $RETVAL -ne 0 ]] && symbols+="%{%F{red}%}✘" 195 | [[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}⚡" 196 | [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙" 197 | 198 | [[ -n "$symbols" ]] && prompt_segment $SHELLDER_STATUS_BG $SHELLDER_STATUS_FG "$symbols" 199 | } 200 | 201 | 202 | # 203 | # Prompt 204 | # 205 | build_prompt() { 206 | RETVAL=$? 207 | prompt_status 208 | prompt_virtualenv 209 | prompt_context 210 | prompt_dir 211 | prompt_git 212 | prompt_hg 213 | prompt_end 214 | } 215 | setopt prompt_subst 216 | PROMPT='%{%f%b%k%}$(build_prompt) ' 217 | -------------------------------------------------------------------------------- /prompts/prompt_toasty_setup: -------------------------------------------------------------------------------- 1 | setopt promptsubst 2 | 3 | autoload -Uz add-zsh-hook 4 | autoload -Uz vcs_info 5 | 6 | zstyle ':vcs_info:*' enable git 7 | zstyle ':vcs_info:*' check-for-changes false 8 | zstyle ':vcs_info:*' actionformats \ 9 | '%B%F{cyan}(%b)%f%%b%c' 10 | zstyle ':vcs_info:*' formats \ 11 | '%B%F{cyan}(%b)%f%%b%c' 12 | 13 | # we want to catch unstaged and untracked in one swoop 14 | zstyle ':vcs_info:git*+set-message:*' hooks git-dirty 15 | function +vi-git-dirty { 16 | if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] 17 | then 18 | if git status --porcelain | grep -Fq -e 'A' -e '??' -e 'M' 19 | then 20 | hook_com[staged]+=' %B%F{yellow}✗%f%b ' 21 | else 22 | hook_com[staged]+=' %B%F{green}✔%f%b ' 23 | fi 24 | fi 25 | } 26 | 27 | add-zsh-hook -Uz precmd vcs_info 28 | 29 | PS1='%B%(?.%F{green}.%F{red})→%f%b %B%(!.%F{red}.%F{blue})%1~%f%b ${vcs_info_msg_0_}' 30 | RPS1='%B%F{red}[%n@%m]%f%b' 31 | 32 | # vim: ft=zsh 33 | -------------------------------------------------------------------------------- /source/aliases.zsh: -------------------------------------------------------------------------------- 1 | # Various common aliases 2 | 3 | alias grep='grep --color=auto' 4 | alias ls='ls --color=auto' ll='ls -lh' l=ll la='l -a' 5 | -------------------------------------------------------------------------------- /source/bindkeys.zsh: -------------------------------------------------------------------------------- 1 | # this is totally not shamelessly stolen from omz-keybindings and zshwiki 2 | # definitely not 3 | # no way 4 | 5 | # basic bindings 6 | bindkey ${terminfo[khome]} beginning-of-line 7 | bindkey ${terminfo[kend]} end-of-line 8 | 9 | bindkey ${terminfo[kdch1]} delete-char 10 | 11 | bindkey ${terminfo[kcuu1]} up-line-or-history 12 | bindkey ${terminfo[kcud1]} down-line-or-history 13 | 14 | bindkey ${terminfo[kcub1]} backward-char 15 | bindkey ${terminfo[kcuf1]} forward-char 16 | 17 | bindkey '^?' backward-delete-char 18 | bindkey ${terminfo[kdch1]} delete-char 19 | 20 | # opinionated bindings 21 | bindkey '^r' history-incremental-search-backward 22 | bindkey '^[[1;5C' forward-word 23 | bindkey '^[[1;5D' backward-word 24 | bindkey ${terminfo[kcbt]} reverse-menu-complete 25 | 26 | # make sure terminfo keys make actual sense 27 | if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then 28 | function zle-line-init () { 29 | echoti smkx 30 | } 31 | function zle-line-finish () { 32 | echoti rmkx 33 | } 34 | zle -N zle-line-init 35 | zle -N zle-line-finish 36 | fi 37 | -------------------------------------------------------------------------------- /source/completions.zsh: -------------------------------------------------------------------------------- 1 | zstyle ':completion:*' list-colors "${(@s.:.)LS_COLORS}" 2 | 3 | zstyle ':completion*:cd:*' tag-order local-directories directory-stack path-directories 4 | zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*' 5 | -------------------------------------------------------------------------------- /source/env.zsh: -------------------------------------------------------------------------------- 1 | # various environment variables that should generally make sense 2 | 3 | # primarily for embedded environments with busybox etc, avoid nano. do override 4 | EDITOR=vi 5 | 6 | PAGER=less 7 | READNULLCMD=less 8 | 9 | export EDITOR 10 | -------------------------------------------------------------------------------- /source/options.zsh: -------------------------------------------------------------------------------- 1 | # changing directories 2 | setopt autocd 3 | setopt noautopushd 4 | setopt pushdignoredups 5 | setopt pushdtohome 6 | 7 | # completion 8 | setopt alwaystoend 9 | 10 | # expansion and globbing 11 | 12 | # history 13 | setopt histexpiredupsfirst 14 | setopt histignoredups 15 | setopt histignorespace 16 | setopt histverify 17 | setopt incappendhistory 18 | 19 | # initialization 20 | setopt noallexport 21 | 22 | # i/o 23 | setopt noflowcontrol 24 | setopt interactivecomments 25 | 26 | # job control 27 | setopt longlistjobs 28 | 29 | # prompting 30 | setopt promptsubst 31 | # setopt transientrprompt ? 32 | 33 | # scripts and functions 34 | 35 | # shell emulation 36 | setopt bsdecho 37 | -------------------------------------------------------------------------------- /third-party-licenses/LICENSE.OMZ.md: -------------------------------------------------------------------------------- 1 | Some of the items included here are directly taken from Oh-My-Zsh. This license applies to them. 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2009-2016 Robby Russell and contributors 6 | See the full list at https://github.com/robbyrussell/oh-my-zsh/contributors 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /third-party-licenses/LICENSE.pure.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /third-party-licenses/LICENSE.purer.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /third-party-licenses/LICENSE.shellder.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Hyeon Kim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /zshenv: -------------------------------------------------------------------------------- 1 | emulate sh -c 'source /etc/profile' 2 | -------------------------------------------------------------------------------- /zshrc: -------------------------------------------------------------------------------- 1 | print -Pv zrc %N # get current file location, store it in $zrc 2 | zrc=$zrc:A # resolve $zrc (assume path) to its absolute location 3 | zshd=${zrc:h} 4 | 5 | : ${zd:=$HOME/.zsh} 6 | 7 | # spath -> autosource path, ala plugins 8 | # apath -> sourceall path, for .d dirs 9 | typeset -T SPATH spath 10 | typeset -T APATH apath 11 | 12 | # default values 13 | spath=( 14 | $zd/plugins 15 | $zshd/plugins 16 | ) 17 | apath=( 18 | $zshd/source 19 | $zd/source 20 | ) 21 | 22 | # user stuff comes first 23 | # completions come after the functions they complete 24 | fpath+=( 25 | $zd/functions 26 | $zd/completions 27 | $zd/prompts 28 | $zshd/functions 29 | $zshd/completions 30 | $zshd/prompts 31 | ) 32 | # you can use your functions as standalone scripts without autoloading them 33 | # just +x 34 | path+=( 35 | $zd/functions 36 | ) 37 | 38 | # sourced before sourcealling 39 | # should be the location to edit fpath/apath/spath 40 | [[ -f $zd/pre ]] && . $zd/pre 41 | 42 | # allow digest drop-in 43 | if [[ -d $zd/digests ]]; then 44 | local f= 45 | for f in $zd/digests/*.zwc(N); do 46 | fpath+=( $f ) 47 | autoload -w $f 48 | done 49 | fi 50 | 51 | autoload sourceall 52 | sourceall zsh # source every .zsh file in every $apath[@] directory 53 | 54 | # local zshrc 55 | [[ -f $zd/zshrc.local ]] && . $zd/zshrc.local 56 | 57 | # LITERALLY THE VERY LAST THING WE DO IS COMPINIT PLS DUN DO IT URSELF 58 | autoload -Uz compinit 59 | compinit 60 | 61 | # vim: ft=zsh 62 | --------------------------------------------------------------------------------