diff options
Diffstat (limited to 'dot_local/share')
30 files changed, 2379 insertions, 0 deletions
diff --git a/dot_local/share/toasty/.gitignore b/dot_local/share/toasty/.gitignore new file mode 100644 index 0000000..3368c47 --- /dev/null +++ b/dot_local/share/toasty/.gitignore @@ -0,0 +1,4 @@ +*.zwc +.zcompdump +.zdirs +.zprofile diff --git a/dot_local/share/toasty/.gitmodules b/dot_local/share/toasty/.gitmodules new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dot_local/share/toasty/.gitmodules diff --git a/dot_local/share/toasty/.zshrc b/dot_local/share/toasty/.zshrc new file mode 120000 index 0000000..c8b42f4 --- /dev/null +++ b/dot_local/share/toasty/.zshrc @@ -0,0 +1 @@ +zshrc
\ No newline at end of file diff --git a/dot_local/share/toasty/LICENSE.md b/dot_local/share/toasty/LICENSE.md new file mode 100644 index 0000000..2a7b602 --- /dev/null +++ b/dot_local/share/toasty/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 Chloe Kudryavtsev + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dot_local/share/toasty/README.adoc b/dot_local/share/toasty/README.adoc new file mode 100644 index 0000000..8ecf7dd --- /dev/null +++ b/dot_local/share/toasty/README.adoc @@ -0,0 +1,202 @@ +:icons: font +:source-highlighter: pygments +:toc: preamble + +// Links +:license: LICENSE.md +:example: examples/zshrc.local + +:img-license: https://img.shields.io/github/license/5pacetoast/toasty-zsh.svg + +:license-omz: third-party-licenses/LICENSE.OMZ.md +:license-pure: third-party-licenses/LICENSE.pure.txt +:license-purer: third-party-licenses/LICENSE.purer.txt +:license-shellder: third-party-licenses/LICENSE.shellder.txt + +:repo-omz: https://github.com/robbyrussell/oh-my-zsh[Oh My Zsh] +:repo-pure: https://github.com/sindresorhus/pure[Pure] +:repo-purer: https://github.com/dfurnes/purer[Purer] +:repo-shellder: https://github.com/simnalamburt/shellder[Shellder] + +:brpaste: https://brpaste.xyz[BRPaste] +:sprunge: http://sprunge.us[Sprunge] +:zshwiki: http://zshwiki.org[Zsh Wiki] + +// Github-specific workarounds +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +endif::[] + += Toasty Zsh + +image::{img-license}[License, link={license}] + +The zsh framework made to facilitate management, not dictate it. + +== Quickstart +1. Clone this repository somewhere +2. `echo ZDOTDIR=/path/to/repo > ~/.zshenv` +3. Restart your z shell + +=== Expanding on it +- Drop files you want to be sourced during startup into `$zd/source/` +- Drop functions you want to potentially use/autoload into `$zd/functions/` +- Drop plugins you want to source on-demand into `$zd/plugins/` +- Drop custom completions into `$zd/completions/` +- Drop custom prompts into `$zd/prompts/` + +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. + +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. + +NOTE: If you want `$zd` to be anywhere but `~/.zsh`, set it in `~/.zshenv`. + +=== Full List of Supported Installation Methods +==== Local +- Setting `$ZDOTDIR` (usually in `~/.zshenv`) to point to the repository. ++ +WARNING: This may, however, dirty your repository, depending on what `$ZDOTDIR` ends up being used for. +- Sourcing `zshrc` in `~/.zshrc`. +- Linking `zshrc` to `~/.zshrc`. + +==== Global +- Cloning toasty-zsh into `/etc/zsh` (on distributions with sane/common zsh defaults). +- Sourcing `zshrc` in `/etc/zsh/zshrc`. +- Linking `zshrc` to `/etc/zsh/zshrc`. + +WARNING: You may want to `touch ~/.zshrc` when installing globally, to avoid zsh complaining about it being missing. + +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. + +== Structure +Toasty Zsh provides a structure under `~/.zsh` for you to use. Everything except `zshrc.local` is optional. + +`$zshd`:: Dynamically set to wherever the Toasty Zsh repository copy is. +`$zd`:: Your "Z Directory". Set to `~/.zsh` by default. The rest of this list is inside of this. +completions/:: Added to your `$fpath`. Serves to put custom completions into without muddying up your functions. +functions/:: Added to your `$fpath`. Serves to put your functions into, for autoloading. +plugins/:: Added to your `$spath`. Serves for optionally-sourced plugins. See <<Autosource>> for more details. +prompts/:: Added to your `$fpath`. Serves to throw your custom prompts into without muddying up your functions. +source/:: Added to your `$apath`. Serves for automatic sourcing on-demand/startup. See <<Sourceall>> for more details. +pre:: Sourced right before running <<Sourceall>>, giving you a chance to edit the sane-default `${fpath,spath,apath}` values. +zshrc.local:: Sourced right before compinit. + +== API +=== Functions +Various functions for use. Some are used internally. + +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. + +==== Autosource +Iterates over `$spath` until it finds a matching `$1`, then sources it. + +Examples: +[source,sh] +---- +spath=( "$PWD" ) autosource a # <1> +spath=( a b ) autosource c # <2> +spath=( "$PWD" ) autosource foo/bar # <3> +---- +<1> will source `./a` if it exists +<2> will source `./a/c`, and then `./b/c` if that fails +<3> will source `./foo/bar` if it exists + +WARNING: Relative entries will always be relative. If you set `$spath` to `.` and then change directories, it'll continue using `.`, which is potentially insecure. + +NOTE: For convenience's sake, `$spath` and `$SPATH` are bound - you can manipulate `$SPATH` (which is formatted like `$PATH`) to modify `$spath`. + +==== Sourceall +Will source every file in every directory in `$apath`. If an argument is provided, will only source files that end in `.$1`. + +Examples: +[source,sh] +---- +apath=( a b ) sourceall # <1> +apath=( "$PWD" ) sourceall zsh # <2> +---- +<1> will source `a/*` and `b/*` +<2> will source `./*.zsh` + +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`. + +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. + +NOTE: As with `$spath`, `$apath` is bound to a `$PATH`-like `$APATH`. + +==== Brpaste +Simple wrapper around {brpaste}. +Takes thing in stdin, or uses the first argument as the file to upload. +Outputs the paste url. + +Examples: +[source,sh] +---- +echo hi | brpaste # <1> +brpaste file # <2> +bsdtar -cf- --format shar dir | brpaste # <3> +brpaste file | xsel -ib # <4> +---- +<1> upload "hi\n" to brpaste.xyz +<2> upload file to brpaste.xyz +<3> upload a "shar" archive of dir to brpaste.xyz +<4> upload file to brpaste.xyz and place the non-raw link into the clipboard + +==== Sprunge +Simple wrapper around {sprunge}. Takes things in stdin, outputs the url into stdout. + +Examples: +[source,sh] +---- +echo hi | sprunge # <1> +sprunge < file # <2> +bsdtar -cf - --format shar dir | sprunge # <3> +---- +<1> upload `"hi\n"` to sprunge.us +<2> upload file to sprunge.us +<3> upload a "shar" archive of dir to sprunge.us + +=== Plugins +Plugins are just files you source! See <<Autosource>> for a convenient way to do so. + +You can add your own by dropping them into a directory in your `$spath` (such as `$zd/plugins`). + +==== Sudo +Press `<esc>` twice to either add or remove `sudo` from the beginning of your line. + +If the current line is empty, operates on the previous line instead. + +==== Xterm-Title +Sets up a simple hook system to print what's currently being executed into an xterm-compatible terminal's title. + +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. + +== External Projects +I didn't write everything in here, some of it is bundled. + +Note that you do not pay (except with drive space) for most of these unless you choose to use them. + +=== Plugins + +Sudo:: From {repo-omz}. + +=== Prompts + +Pure:: From {repo-pure}. +Purer:: From {repo-purer}. +Shellder:: From {repo-shellder}. +Toasty:: Written from scratch by me, but takes heavy inspiration from robbyrussel's theme from {repo-omz}. + +=== Other + +`bindkeys.zsh`:: Written by me, but heavily inspired by similar content from {repo-omz} and the {zshwiki}. + +=== Licenses + +- Oh My Zsh link:{license-omz}[LICENSE] +- Pure link:{license-pure}[LICENSE] +- Purer link:{license-purer}[LICENSE] +- Shellder link:{license-shellder}[LICENSE] diff --git a/dot_local/share/toasty/completions/_autosource b/dot_local/share/toasty/completions/_autosource new file mode 100644 index 0000000..35dbccd --- /dev/null +++ b/dot_local/share/toasty/completions/_autosource @@ -0,0 +1,10 @@ +#compdef autosource + +function _autosource { + _alternative \ + 'plugin:filename:{_path_files -W spath}' +} + +_autosource + +# vim: ft=zsh diff --git a/dot_local/share/toasty/examples/source/enable-shared-history.zsh b/dot_local/share/toasty/examples/source/enable-shared-history.zsh new file mode 100644 index 0000000..9b29c96 --- /dev/null +++ b/dot_local/share/toasty/examples/source/enable-shared-history.zsh @@ -0,0 +1,5 @@ +setopt sharehistory + +HISTSIZE=1000 +SAVEHIST=1000 +HISTFILE="$zd/history" diff --git a/dot_local/share/toasty/examples/zshrc.local b/dot_local/share/toasty/examples/zshrc.local new file mode 100644 index 0000000..6b74342 --- /dev/null +++ b/dot_local/share/toasty/examples/zshrc.local @@ -0,0 +1,11 @@ +# example of a basic local zshrc + +autoload autosource # for plugins +autosource sudo # enable sudo plugin + +autoload sprunge # something with output | sprunge, and sprunge < somefile + +# get a non-default prompt +autoload -Uz promptinit +promptinit +prompt pure diff --git a/dot_local/share/toasty/functions/autosource b/dot_local/share/toasty/functions/autosource new file mode 100644 index 0000000..12b6bab --- /dev/null +++ b/dot_local/share/toasty/functions/autosource @@ -0,0 +1,10 @@ +#!/bin/zsh +local f= +local p= +for f +do + for p in $spath + do + [[ -r $p/$f ]] && . "$p/$f" && break + done +done diff --git a/dot_local/share/toasty/functions/brpaste b/dot_local/share/toasty/functions/brpaste new file mode 100644 index 0000000..b4cf5ef --- /dev/null +++ b/dot_local/share/toasty/functions/brpaste @@ -0,0 +1,6 @@ +#!/bin/zsh +host='https://brpaste.xyz' +[[ $# -eq 0 ]] && set -- '-' +brpaste_id=$(curl -\#fF "data=<$1" "$host") \ + || { echo 'ERROR: Upload failed!' >&2 && return 1; } +printf '%s/%s\n' "$host" "$brpaste_id" diff --git a/dot_local/share/toasty/functions/sourceall b/dot_local/share/toasty/functions/sourceall new file mode 100644 index 0000000..b63c58b --- /dev/null +++ b/dot_local/share/toasty/functions/sourceall @@ -0,0 +1,16 @@ +#!/bin/zsh +local ext= +local f= +local p= + +# calling `sourceall zsh` will source *.zsh, otherwise * +(( $# > 0 )) && ext=".$1" + +for p in $apath +do + [[ -d $p ]] || continue + for f in $p/*$ext(.N) + do + [[ -r $f ]] && . $f + done +done diff --git a/dot_local/share/toasty/functions/sprunge b/dot_local/share/toasty/functions/sprunge new file mode 100644 index 0000000..c76ed76 --- /dev/null +++ b/dot_local/share/toasty/functions/sprunge @@ -0,0 +1,2 @@ +#!/bin/zsh +curl -F 'sprunge=<-' http://sprunge.us diff --git a/dot_local/share/toasty/plugins/sudo b/dot_local/share/toasty/plugins/sudo new file mode 100644 index 0000000..0ba8bed --- /dev/null +++ b/dot_local/share/toasty/plugins/sudo @@ -0,0 +1,25 @@ +# ------------------------------------------------------------------------------ +# Description +# ----------- +# +# sudo will be inserted before the command +# +# ------------------------------------------------------------------------------ +# Authors +# ------- +# +# * Dongweiming <ciici123@gmail.com> +# +# ------------------------------------------------------------------------------ + +sudo-command-line() { + [[ -z $BUFFER ]] && zle up-history + if [[ $BUFFER == sudo\ * ]]; then + LBUFFER="${LBUFFER#sudo }" + else + LBUFFER="sudo $LBUFFER" + fi +} +zle -N sudo-command-line +# Defined shortcut keys: [Esc] [Esc] +bindkey "\e\e" sudo-command-line diff --git a/dot_local/share/toasty/plugins/xterm-title b/dot_local/share/toasty/plugins/xterm-title new file mode 100644 index 0000000..a57ea03 --- /dev/null +++ b/dot_local/share/toasty/plugins/xterm-title @@ -0,0 +1,10 @@ +function xterm-title { + print -Pn '\e]0;%n@%m $(history $HISTCMD | cut -b8-)\a' +} + +autoload -Uz add-zsh-hook +add-zsh-hook precmd xterm-title +add-zsh-hook preexec xterm-title +add-zsh-hook chpwd xterm-title + +# vim: ft=zsh diff --git a/dot_local/share/toasty/prompts/async b/dot_local/share/toasty/prompts/async new file mode 100644 index 0000000..d35f128 --- /dev/null +++ b/dot_local/share/toasty/prompts/async @@ -0,0 +1,547 @@ +#!/usr/bin/env zsh + +# +# zsh-async +# +# version: 1.7.0 +# author: Mathias Fredriksson +# url: https://github.com/mafredri/zsh-async +# + +typeset -g ASYNC_VERSION=1.7.0 +# Produce debug output from zsh-async when set to 1. +typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0} + +# Execute commands that can manipulate the environment inside the async worker. Return output via callback. +_async_eval() { + local ASYNC_JOB_NAME + # Rename job to _async_eval and redirect all eval output to cat running + # in _async_job. Here, stdout and stderr are not separated for + # simplicity, this could be improved in the future. + { + eval "$@" + } &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'cat') +} + +# Wrapper for jobs executed by the async worker, gives output in parseable format with execution time +_async_job() { + # Disable xtrace as it would mangle the output. + setopt localoptions noxtrace + + # Store start time for job. + float -F duration=$EPOCHREALTIME + + # Run the command and capture both stdout (`eval`) and stderr (`cat`) in + # separate subshells. When the command is complete, we grab write lock + # (mutex token) and output everything except stderr inside the command + # block, after the command block has completed, the stdin for `cat` is + # closed, causing stderr to be appended with a $'\0' at the end to mark the + # end of output from this job. + local jobname=${ASYNC_JOB_NAME:-$1} + local stdout stderr ret tok + { + stdout=$(eval "$@") + ret=$? + duration=$(( EPOCHREALTIME - duration )) # Calculate duration. + + # Grab mutex lock, stalls until token is available. + read -r -k 1 -p tok || exit 1 + + # Return output (<job_name> <return_code> <stdout> <duration> <stderr>). + print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration + } 2> >(stderr=$(cat) && print -r -n - " "${(q)stderr}$'\0') + + # Unlock mutex by inserting a token. + print -n -p $tok +} + +# The background worker manages all tasks and runs them without interfering with other processes +_async_worker() { + # Reset all options to defaults inside async worker. + emulate -R zsh + + # Make sure monitor is unset to avoid printing the + # pids of child processes. + unsetopt monitor + + # Redirect stderr to `/dev/null` in case unforseen errors produced by the + # worker. For example: `fork failed: resource temporarily unavailable`. + # Some older versions of zsh might also print malloc errors (know to happen + # on at least zsh 5.0.2 and 5.0.8) likely due to kill signals. + exec 2>/dev/null + + # When a zpty is deleted (using -d) all the zpty instances created before + # the one being deleted receive a SIGHUP, unless we catch it, the async + # worker would simply exit (stop working) even though visible in the list + # of zpty's (zpty -L). + TRAPHUP() { + return 0 # Return 0, indicating signal was handled. + } + + local -A storage + local unique=0 + local notify_parent=0 + local parent_pid=0 + local coproc_pid=0 + local processing=0 + + local -a zsh_hooks zsh_hook_functions + zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory) + zsh_hook_functions=(${^zsh_hooks}_functions) + unfunction $zsh_hooks &>/dev/null # Deactivate all zsh hooks inside the worker. + unset $zsh_hook_functions # And hooks with registered functions. + unset zsh_hooks zsh_hook_functions # Cleanup. + + child_exit() { + local -a pids + pids=(${${(v)jobstates##*:*:}%\=*}) + + # If coproc (cat) is the only child running, we close it to avoid + # leaving it running indefinitely and cluttering the process tree. + if (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then + coproc : + coproc_pid=0 + fi + + # On older version of zsh (pre 5.2) we notify the parent through a + # SIGWINCH signal because `zpty` did not return a file descriptor (fd) + # prior to that. + if (( notify_parent )); then + # We use SIGWINCH for compatibility with older versions of zsh + # (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could + # cause a deadlock in the shell under certain circumstances. + kill -WINCH $parent_pid + fi + } + + # Register a SIGCHLD trap to handle the completion of child processes. + trap child_exit CHLD + + # Process option parameters passed to worker + while getopts "np:u" opt; do + case $opt in + n) notify_parent=1;; + p) parent_pid=$OPTARG;; + u) unique=1;; + esac + done + + killjobs() { + local tok + local -a pids + pids=(${${(v)jobstates##*:*:}%\=*}) + + # No need to send SIGHUP if no jobs are running. + (( $#pids == 0 )) && continue + (( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue + + # Grab lock to prevent half-written output in case a child + # process is in the middle of writing to stdin during kill. + (( coproc_pid )) && read -r -k 1 -p tok + + kill -HUP -$$ # Send to entire process group. + coproc : # Quit coproc. + coproc_pid=0 # Reset pid. + } + + local request do_eval=0 + local -a cmd + while :; do + # Wait for jobs sent by async_job. + read -r -d $'\0' request || { + # Since we handle SIGHUP above (and thus do not know when `zpty -d`) + # occurs, a failure to read probably indicates that stdin has + # closed. This is why we propagate the signal to all children and + # exit manually. + kill -HUP -$$ # Send SIGHUP to all jobs. + exit 0 + } + + # Check for non-job commands sent to worker + case $request in + _unset_trap) notify_parent=0; continue;; + _killjobs) killjobs; continue;; + _async_eval*) do_eval=1;; + esac + + # Parse the request using shell parsing (z) to allow commands + # to be parsed from single strings and multi-args alike. + cmd=("${(z)request}") + + # Name of the job (first argument). + local job=$cmd[1] + + # If worker should perform unique jobs + if (( unique )); then + # Check if a previous job is still running, if yes, let it finnish + for pid in ${${(v)jobstates##*:*:}%\=*}; do + if [[ ${storage[$job]} == $pid ]]; then + continue 2 + fi + done + fi + + # Guard against closing coproc from trap before command has started. + processing=1 + + # Because we close the coproc after the last job has completed, we must + # recreate it when there are no other jobs running. + if (( ! coproc_pid )); then + # Use coproc as a mutex for synchronized output between children. + coproc cat + coproc_pid="$!" + # Insert token into coproc + print -n -p "t" + fi + + if (( do_eval )); then + shift cmd # Strip _async_eval from cmd. + _async_eval $cmd + do_eval=0 + else + # Run job in background, completed jobs are printed to stdout. + _async_job $cmd & + # Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')... + storage[$job]="$!" + fi + + processing=0 # Disable guard. + done +} + +# +# Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the +# job name, return code, output and execution time and with minimal effort. +# +# If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job +# name), non-zero return code and fifth argument describing the error (stderr). +# +# usage: +# async_process_results <worker_name> <callback_function> +# +# callback_function is called with the following parameters: +# $1 = job name, e.g. the function passed to async_job +# $2 = return code +# $3 = resulting stdout from execution +# $4 = execution time, floating point e.g. 2.05 seconds +# $5 = resulting stderr from execution +# $6 = has next result in buffer (0 = buffer empty, 1 = yes) +# +async_process_results() { + setopt localoptions unset noshwordsplit noksharrays noposixidentifiers noposixstrings + + local worker=$1 + local callback=$2 + local caller=$3 + local -a items + local null=$'\0' data + integer -l len pos num_processed has_next + + typeset -gA ASYNC_PROCESS_BUFFER + + # Read output from zpty and parse it if available. + while zpty -r -t $worker data 2>/dev/null; do + ASYNC_PROCESS_BUFFER[$worker]+=$data + len=${#ASYNC_PROCESS_BUFFER[$worker]} + pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). + + # Keep going until we find a NULL-character. + if (( ! len )) || (( pos > len )); then + continue + fi + + while (( pos <= len )); do + # Take the content from the beginning, until the NULL-character and + # perform shell parsing (z) and unquoting (Q) as an array (@). + items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}") + + # Remove the extracted items from the buffer. + ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]} + + len=${#ASYNC_PROCESS_BUFFER[$worker]} + if (( len > 1 )); then + pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). + fi + + has_next=$(( len != 0 )) + if (( $#items == 5 )); then + items+=($has_next) + $callback "${(@)items}" # Send all parsed items to the callback. + (( num_processed++ )) + elif [[ -z $items ]]; then + # Empty items occur between results due to double-null ($'\0\0') + # caused by commands being both pre and suffixed with null. + else + # In case of corrupt data, invoke callback with *async* as job + # name, non-zero exit status and an error message on stderr. + $callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next + fi + done + done + + (( num_processed )) && return 0 + + # Avoid printing exit value when `setopt printexitvalue` is active.` + [[ $caller = trap || $caller = watcher ]] && return 0 + + # No results were processed + return 1 +} + +# Watch worker for output +_async_zle_watcher() { + setopt localoptions noshwordsplit + typeset -gA ASYNC_PTYS ASYNC_CALLBACKS + local worker=$ASYNC_PTYS[$1] + local callback=$ASYNC_CALLBACKS[$worker] + + if [[ -n $callback ]]; then + async_process_results $worker $callback watcher + fi +} + +# +# Start a new asynchronous job on specified worker, assumes the worker is running. +# +# usage: +# async_job <worker_name> <my_function> [<function_params>] +# +async_job() { + setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings + + local worker=$1; shift + + local -a cmd + cmd=("$@") + if (( $#cmd > 1 )); then + cmd=(${(q)cmd}) # Quote special characters in multi argument commands. + fi + + # Quote the cmd in case RC_EXPAND_PARAM is set. + zpty -w $worker "$cmd"$'\0' +} + +# +# Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example, +# issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs. +# +# Output will be returned via callback, job name will be [async/eval]. +# +# usage: +# async_worker_eval <worker_name> <my_function> [<function_params>] +# +async_worker_eval() { + setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings + + local worker=$1; shift + + local -a cmd + cmd=("$@") + if (( $#cmd > 1 )); then + cmd=(${(q)cmd}) # Quote special characters in multi argument commands. + fi + + # Quote the cmd in case RC_EXPAND_PARAM is set. + zpty -w $worker "_async_eval $cmd"$'\0' +} + +# This function traps notification signals and calls all registered callbacks +_async_notify_trap() { + setopt localoptions noshwordsplit + + local k + for k in ${(k)ASYNC_CALLBACKS}; do + async_process_results $k ${ASYNC_CALLBACKS[$k]} trap + done +} + +# +# Register a callback for completed jobs. As soon as a job is finnished, async_process_results will be called with the +# specified callback function. This requires that a worker is initialized with the -n (notify) option. +# +# usage: +# async_register_callback <worker_name> <callback_function> +# +async_register_callback() { + setopt localoptions noshwordsplit nolocaltraps + + typeset -gA ASYNC_CALLBACKS + local worker=$1; shift + + ASYNC_CALLBACKS[$worker]="$*" + + # Enable trap when the ZLE watcher is unavailable, allows + # workers to notify (via -n) when a job is done. + if [[ ! -o interactive ]] || [[ ! -o zle ]]; then + trap '_async_notify_trap' WINCH + fi +} + +# +# Unregister the callback for a specific worker. +# +# usage: +# async_unregister_callback <worker_name> +# +async_unregister_callback() { + typeset -gA ASYNC_CALLBACKS + + unset "ASYNC_CALLBACKS[$1]" +} + +# +# Flush all current jobs running on a worker. This will terminate any and all running processes under the worker, use +# with caution. +# +# usage: +# async_flush_jobs <worker_name> +# +async_flush_jobs() { + setopt localoptions noshwordsplit + + local worker=$1; shift + + # Check if the worker exists + zpty -t $worker &>/dev/null || return 1 + + # Send kill command to worker + async_job $worker "_killjobs" + + # Clear the zpty buffer. + local junk + if zpty -r -t $worker junk '*'; then + (( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}" + while zpty -r -t $worker junk '*'; do + (( ASYNC_DEBUG )) && print -n "${(V)junk}" + done + (( ASYNC_DEBUG )) && print + fi + + # Finally, clear the process buffer in case of partially parsed responses. + typeset -gA ASYNC_PROCESS_BUFFER + unset "ASYNC_PROCESS_BUFFER[$worker]" +} + +# +# Start a new async worker with optional parameters, a worker can be told to only run unique tasks and to notify a +# process when tasks are complete. +# +# usage: +# async_start_worker <worker_name> [-u] [-n] [-p <pid>] +# +# opts: +# -u unique (only unique job names can run) +# -n notify through SIGWINCH signal +# -p pid to notify (defaults to current pid) +# +async_start_worker() { + setopt localoptions noshwordsplit + + local worker=$1; shift + zpty -t $worker &>/dev/null && return + + typeset -gA ASYNC_PTYS + typeset -h REPLY + typeset has_xtrace=0 + + # Make sure async worker is started without xtrace + # (the trace output interferes with the worker). + [[ -o xtrace ]] && { + has_xtrace=1 + unsetopt xtrace + } + + if (( ! ASYNC_ZPTY_RETURNS_FD )) && [[ -o interactive ]] && [[ -o zle ]]; then + # When zpty doesn't return a file descriptor (on older versions of zsh) + # we try to guess it anyway. + integer -l zptyfd + exec {zptyfd}>&1 # Open a new file descriptor (above 10). + exec {zptyfd}>&- # Close it so it's free to be used by zpty. + fi + + zpty -b $worker _async_worker -p $$ $@ || { + async_stop_worker $worker + return 1 + } + + # Re-enable it if it was enabled, for debugging. + (( has_xtrace )) && setopt xtrace + + if [[ $ZSH_VERSION < 5.0.8 ]]; then + # For ZSH versions older than 5.0.8 we delay a bit to give + # time for the worker to start before issuing commands, + # otherwise it will not be ready to receive them. + sleep 0.001 + fi + + if [[ -o interactive ]] && [[ -o zle ]]; then + if (( ! ASYNC_ZPTY_RETURNS_FD )); then + REPLY=$zptyfd # Use the guessed value for the file desciptor. + fi + + ASYNC_PTYS[$REPLY]=$worker # Map the file desciptor to the worker. + zle -F $REPLY _async_zle_watcher # Register the ZLE handler. + + # Disable trap in favor of ZLE handler when notify is enabled (-n). + async_job $worker _unset_trap + fi +} + +# +# Stop one or multiple workers that are running, all unfetched and incomplete work will be lost. +# +# usage: +# async_stop_worker <worker_name_1> [<worker_name_2>] +# +async_stop_worker() { + setopt localoptions noshwordsplit + + local ret=0 worker k v + for worker in $@; do + # Find and unregister the zle handler for the worker + for k v in ${(@kv)ASYNC_PTYS}; do + if [[ $v == $worker ]]; then + zle -F $k + unset "ASYNC_PTYS[$k]" + fi + done + async_unregister_callback $worker + zpty -d $worker 2>/dev/null || ret=$? + + # Clear any partial buffers. + typeset -gA ASYNC_PROCESS_BUFFER + unset "ASYNC_PROCESS_BUFFER[$worker]" + done + + return $ret +} + +# +# Initialize the required modules for zsh-async. To be called before using the zsh-async library. +# +# usage: +# async_init +# +async_init() { + (( ASYNC_INIT_DONE )) && return + typeset -g ASYNC_INIT_DONE=1 + + zmodload zsh/zpty + zmodload zsh/datetime + + # Check if zsh/zpty returns a file descriptor or not, + # shell must also be interactive with zle enabled. + typeset -g ASYNC_ZPTY_RETURNS_FD=0 + [[ -o interactive ]] && [[ -o zle ]] && { + typeset -h REPLY + zpty _async_test : + (( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1 + zpty -d _async_test + } +} + +async() { + async_init +} + +async "$@" diff --git a/dot_local/share/toasty/prompts/prompt_pure_setup b/dot_local/share/toasty/prompts/prompt_pure_setup new file mode 100644 index 0000000..a313c83 --- /dev/null +++ b/dot_local/share/toasty/prompts/prompt_pure_setup @@ -0,0 +1,620 @@ +# Pure +# by Sindre Sorhus +# https://github.com/sindresorhus/pure +# MIT License + +# For my own and others sanity +# git: +# %b => current branch +# %a => current action (rebase/merge) +# prompt: +# %F => color dict +# %f => reset color +# %~ => current path +# %* => time +# %n => username +# %m => shortname host +# %(?..) => prompt conditional - %(condition.true.false) +# terminal codes: +# \e7 => save cursor position +# \e[2A => move cursor 2 lines up +# \e[1G => go to position 1 in terminal +# \e8 => restore cursor position +# \e[K => clears everything after the cursor on the current line +# \e[2K => clear everything on the current line + + +# turns seconds into human readable time +# 165392 => 1d 21h 56m 32s +# https://github.com/sindresorhus/pretty-time-zsh +prompt_pure_human_time_to_var() { + local human total_seconds=$1 var=$2 + local days=$(( total_seconds / 60 / 60 / 24 )) + local hours=$(( total_seconds / 60 / 60 % 24 )) + local minutes=$(( total_seconds / 60 % 60 )) + local seconds=$(( total_seconds % 60 )) + (( days > 0 )) && human+="${days}d " + (( hours > 0 )) && human+="${hours}h " + (( minutes > 0 )) && human+="${minutes}m " + human+="${seconds}s" + + # store human readable time in variable as specified by caller + typeset -g "${var}"="${human}" +} + +# stores (into prompt_pure_cmd_exec_time) the exec time of the last command if set threshold was exceeded +prompt_pure_check_cmd_exec_time() { + integer elapsed + (( elapsed = EPOCHSECONDS - ${prompt_pure_cmd_timestamp:-$EPOCHSECONDS} )) + typeset -g prompt_pure_cmd_exec_time= + (( elapsed > ${PURE_CMD_MAX_EXEC_TIME:-5} )) && { + prompt_pure_human_time_to_var $elapsed "prompt_pure_cmd_exec_time" + } +} + +prompt_pure_set_title() { + setopt localoptions noshwordsplit + + # emacs terminal does not support settings the title + (( ${+EMACS} )) && return + + case $TTY in + # Don't set title over serial console. + /dev/ttyS[0-9]*) return;; + esac + + # Show hostname if connected via ssh. + local hostname= + if [[ -n $prompt_pure_state[username] ]]; then + # Expand in-place in case ignore-escape is used. + hostname="${(%):-(%m) }" + fi + + local -a opts + case $1 in + expand-prompt) opts=(-P);; + ignore-escape) opts=(-r);; + esac + + # Set title atomically in one print statement so that it works + # when XTRACE is enabled. + print -n $opts $'\e]0;'${hostname}${2}$'\a' +} + +prompt_pure_preexec() { + if [[ -n $prompt_pure_git_fetch_pattern ]]; then + # detect when git is performing pull/fetch (including git aliases). + local -H MATCH MBEGIN MEND match mbegin mend + if [[ $2 =~ (git|hub)\ (.*\ )?($prompt_pure_git_fetch_pattern)(\ .*)?$ ]]; then + # we must flush the async jobs to cancel our git fetch in order + # to avoid conflicts with the user issued pull / fetch. + async_flush_jobs 'prompt_pure' + fi + fi + + typeset -g prompt_pure_cmd_timestamp=$EPOCHSECONDS + + # shows the current dir and executed command in the title while a process is active + prompt_pure_set_title 'ignore-escape' "$PWD:t: $2" + + # Disallow python virtualenv from updating the prompt, set it to 12 if + # untouched by the user to indicate that Pure modified it. Here we use + # magic number 12, same as in psvar. + export VIRTUAL_ENV_DISABLE_PROMPT=${VIRTUAL_ENV_DISABLE_PROMPT:-12} +} + +prompt_pure_preprompt_render() { + setopt localoptions noshwordsplit + + # Set color for git branch/dirty status, change color if dirty checking has + # been delayed. + local git_color=242 + [[ -n ${prompt_pure_git_last_dirty_check_timestamp+x} ]] && git_color=red + + # Initialize the preprompt array. + local -a preprompt_parts + + # Set the path. + preprompt_parts+=('%F{blue}%~%f') + + # Add git branch and dirty status info. + typeset -gA prompt_pure_vcs_info + if [[ -n $prompt_pure_vcs_info[branch] ]]; then + preprompt_parts+=("%F{$git_color}"'${prompt_pure_vcs_info[branch]}${prompt_pure_git_dirty}%f') + fi + # Git pull/push arrows. + if [[ -n $prompt_pure_git_arrows ]]; then + preprompt_parts+=('%F{cyan}${prompt_pure_git_arrows}%f') + fi + + # Username and machine, if applicable. + [[ -n $prompt_pure_state[username] ]] && preprompt_parts+=('${prompt_pure_state[username]}') + # Execution time. + [[ -n $prompt_pure_cmd_exec_time ]] && preprompt_parts+=('%F{yellow}${prompt_pure_cmd_exec_time}%f') + + local cleaned_ps1=$PROMPT + local -H MATCH MBEGIN MEND + if [[ $PROMPT = *$prompt_newline* ]]; then + # Remove everything from the prompt until the newline. This + # removes the preprompt and only the original PROMPT remains. + cleaned_ps1=${PROMPT##*${prompt_newline}} + fi + unset MATCH MBEGIN MEND + + # Construct the new prompt with a clean preprompt. + local -ah ps1 + ps1=( + ${(j. .)preprompt_parts} # Join parts, space separated. + $prompt_newline # Separate preprompt and prompt. + $cleaned_ps1 + ) + + PROMPT="${(j..)ps1}" + + # Expand the prompt for future comparision. + local expanded_prompt + expanded_prompt="${(S%%)PROMPT}" + + if [[ $1 == precmd ]]; then + # Initial newline, for spaciousness. + print + elif [[ $prompt_pure_last_prompt != $expanded_prompt ]]; then + # Redraw the prompt. + zle && zle .reset-prompt + fi + + typeset -g prompt_pure_last_prompt=$expanded_prompt +} + +prompt_pure_precmd() { + # check exec time and store it in a variable + prompt_pure_check_cmd_exec_time + unset prompt_pure_cmd_timestamp + + # shows the full path in the title + prompt_pure_set_title 'expand-prompt' '%~' + + # preform async git dirty check and fetch + prompt_pure_async_tasks + + # Check if we should display the virtual env, we use a sufficiently high + # index of psvar (12) here to avoid collisions with user defined entries. + psvar[12]= + # When VIRTUAL_ENV_DISABLE_PROMPT is empty, it was unset by the user and + # Pure should take back control. + if [[ -n $VIRTUAL_ENV ]] && [[ -z $VIRTUAL_ENV_DISABLE_PROMPT || $VIRTUAL_ENV_DISABLE_PROMPT = 12 ]]; then + psvar[12]="${VIRTUAL_ENV:t}" + export VIRTUAL_ENV_DISABLE_PROMPT=12 + fi + + # Make sure VIM prompt is reset. + prompt_pure_reset_prompt_symbol + + # print the preprompt + prompt_pure_preprompt_render "precmd" + + if [[ -n $ZSH_THEME ]]; then + print "WARNING: Oh My Zsh themes are enabled (ZSH_THEME='${ZSH_THEME}'). Pure might not be working correctly." + print "For more information, see: https://github.com/sindresorhus/pure#oh-my-zsh" + unset ZSH_THEME # Only show this warning once. + fi +} + +prompt_pure_async_git_aliases() { + setopt localoptions noshwordsplit + local -a gitalias pullalias + + # list all aliases and split on newline. + gitalias=(${(@f)"$(command git config --get-regexp "^alias\.")"}) + for line in $gitalias; do + parts=(${(@)=line}) # split line on spaces + aliasname=${parts[1]#alias.} # grab the name (alias.[name]) + shift parts # remove aliasname + + # check alias for pull or fetch (must be exact match). + if [[ $parts =~ ^(.*\ )?(pull|fetch)(\ .*)?$ ]]; then + pullalias+=($aliasname) + fi + done + + print -- ${(j:|:)pullalias} # join on pipe (for use in regex). +} + +prompt_pure_async_vcs_info() { + setopt localoptions noshwordsplit + + # configure vcs_info inside async task, this frees up vcs_info + # to be used or configured as the user pleases. + zstyle ':vcs_info:*' enable git + zstyle ':vcs_info:*' use-simple true + # only export two msg variables from vcs_info + zstyle ':vcs_info:*' max-exports 2 + # export branch (%b) and git toplevel (%R) + zstyle ':vcs_info:git*' formats '%b' '%R' + zstyle ':vcs_info:git*' actionformats '%b|%a' '%R' + + vcs_info + + local -A info + info[pwd]=$PWD + info[top]=$vcs_info_msg_1_ + info[branch]=$vcs_info_msg_0_ + + print -r - ${(@kvq)info} +} + +# fastest possible way to check if repo is dirty +prompt_pure_async_git_dirty() { + setopt localoptions noshwordsplit + local untracked_dirty=$1 + + if [[ $untracked_dirty = 0 ]]; then + command git diff --no-ext-diff --quiet --exit-code + else + test -z "$(command git status --porcelain --ignore-submodules -unormal)" + fi + + return $? +} + +prompt_pure_async_git_fetch() { + setopt localoptions noshwordsplit + + # set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+) + export GIT_TERMINAL_PROMPT=0 + # set ssh BachMode to disable all interactive ssh password prompting + export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-"ssh"} -o BatchMode=yes" + + # Default return code, indicates Git fetch failure. + local fail_code=99 + + # Guard against all forms of password prompts. By setting the shell into + # MONITOR mode we can notice when a child process prompts for user input + # because it will be suspended. Since we are inside an async worker, we + # have no way of transmitting the password and the only option is to + # kill it. If we don't do it this way, the process will corrupt with the + # async worker. + setopt localtraps monitor + + # Make sure local HUP trap is unset to allow for signal propagation when + # the async worker is flushed. + trap - HUP + + trap ' + # Unset trap to prevent infinite loop + trap - CHLD + if [[ $jobstates = suspended* ]]; then + # Set fail code to password prompt and kill the fetch. + fail_code=98 + kill %% + fi + ' CHLD + + command git -c gc.auto=0 fetch >/dev/null & + wait $! || return $fail_code + + unsetopt monitor + + # check arrow status after a successful git fetch + prompt_pure_async_git_arrows +} + +prompt_pure_async_git_arrows() { + setopt localoptions noshwordsplit + command git rev-list --left-right --count HEAD...@'{u}' +} + +prompt_pure_async_tasks() { + setopt localoptions noshwordsplit + + # initialize async worker + ((!${prompt_pure_async_init:-0})) && { + async_start_worker "prompt_pure" -u -n + async_register_callback "prompt_pure" prompt_pure_async_callback + typeset -g prompt_pure_async_init=1 + } + + # Update the current working directory of the async worker. + async_worker_eval "prompt_pure" builtin cd -q $PWD + + typeset -gA prompt_pure_vcs_info + + local -H MATCH MBEGIN MEND + if [[ $PWD != ${prompt_pure_vcs_info[pwd]}* ]]; then + # stop any running async jobs + async_flush_jobs "prompt_pure" + + # reset git preprompt variables, switching working tree + unset prompt_pure_git_dirty + unset prompt_pure_git_last_dirty_check_timestamp + unset prompt_pure_git_arrows + unset prompt_pure_git_fetch_pattern + prompt_pure_vcs_info[branch]= + prompt_pure_vcs_info[top]= + fi + unset MATCH MBEGIN MEND + + async_job "prompt_pure" prompt_pure_async_vcs_info + + # # only perform tasks inside git working tree + [[ -n $prompt_pure_vcs_info[top] ]] || return + + prompt_pure_async_refresh +} + +prompt_pure_async_refresh() { + setopt localoptions noshwordsplit + + if [[ -z $prompt_pure_git_fetch_pattern ]]; then + # we set the pattern here to avoid redoing the pattern check until the + # working three has changed. pull and fetch are always valid patterns. + typeset -g prompt_pure_git_fetch_pattern="pull|fetch" + async_job "prompt_pure" prompt_pure_async_git_aliases + fi + + async_job "prompt_pure" prompt_pure_async_git_arrows + + # do not preform git fetch if it is disabled or in home folder. + if (( ${PURE_GIT_PULL:-1} )) && [[ $prompt_pure_vcs_info[top] != $HOME ]]; then + # tell worker to do a git fetch + async_job "prompt_pure" prompt_pure_async_git_fetch + fi + + # if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout + integer time_since_last_dirty_check=$(( EPOCHSECONDS - ${prompt_pure_git_last_dirty_check_timestamp:-0} )) + if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then + unset prompt_pure_git_last_dirty_check_timestamp + # check check if there is anything to pull + async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} + fi +} + +prompt_pure_check_git_arrows() { + setopt localoptions noshwordsplit + local arrows left=${1:-0} right=${2:-0} + + (( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-⇣} + (( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-⇡} + + [[ -n $arrows ]] || return + typeset -g REPLY=$arrows +} + +prompt_pure_async_callback() { + setopt localoptions noshwordsplit + local job=$1 code=$2 output=$3 exec_time=$4 next_pending=$6 + local do_render=0 + + case $job in + prompt_pure_async_vcs_info) + local -A info + typeset -gA prompt_pure_vcs_info + + # parse output (z) and unquote as array (Q@) + info=("${(Q@)${(z)output}}") + local -H MATCH MBEGIN MEND + if [[ $info[pwd] != $PWD ]]; then + # The path has changed since the check started, abort. + return + fi + # check if git toplevel has changed + if [[ $info[top] = $prompt_pure_vcs_info[top] ]]; then + # if stored pwd is part of $PWD, $PWD is shorter and likelier + # to be toplevel, so we update pwd + if [[ $prompt_pure_vcs_info[pwd] = ${PWD}* ]]; then + prompt_pure_vcs_info[pwd]=$PWD + fi + else + # store $PWD to detect if we (maybe) left the git path + prompt_pure_vcs_info[pwd]=$PWD + fi + unset MATCH MBEGIN MEND + + # update has a git toplevel set which means we just entered a new + # git directory, run the async refresh tasks + [[ -n $info[top] ]] && [[ -z $prompt_pure_vcs_info[top] ]] && prompt_pure_async_refresh + + # always update branch and toplevel + prompt_pure_vcs_info[branch]=$info[branch] + prompt_pure_vcs_info[top]=$info[top] + + do_render=1 + ;; + prompt_pure_async_git_aliases) + if [[ -n $output ]]; then + # append custom git aliases to the predefined ones. + prompt_pure_git_fetch_pattern+="|$output" + fi + ;; + prompt_pure_async_git_dirty) + local prev_dirty=$prompt_pure_git_dirty + if (( code == 0 )); then + unset prompt_pure_git_dirty + else + typeset -g prompt_pure_git_dirty="*" + fi + + [[ $prev_dirty != $prompt_pure_git_dirty ]] && do_render=1 + + # When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color. + # To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this + # variable. Thus, only upon next rendering of the preprompt will the result appear in a different color. + (( $exec_time > 5 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS + ;; + prompt_pure_async_git_fetch|prompt_pure_async_git_arrows) + # prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows + # after a successful fetch. + case $code in + 0) + local REPLY + prompt_pure_check_git_arrows ${(ps:\t:)output} + if [[ $prompt_pure_git_arrows != $REPLY ]]; then + typeset -g prompt_pure_git_arrows=$REPLY + do_render=1 + fi + ;; + 99|98) + # Git fetch failed. + ;; + *) + # Non-zero exit status from prompt_pure_async_git_arrows, + # indicating that there is no upstream configured. + if [[ -n $prompt_pure_git_arrows ]]; then + unset prompt_pure_git_arrows + do_render=1 + fi + ;; + esac + ;; + esac + + if (( next_pending )); then + (( do_render )) && typeset -g prompt_pure_async_render_requested=1 + return + fi + + [[ ${prompt_pure_async_render_requested:-$do_render} = 1 ]] && prompt_pure_preprompt_render + unset prompt_pure_async_render_requested +} + +prompt_pure_reset_prompt_symbol() { + prompt_pure_state[prompt]=${PURE_PROMPT_SYMBOL:-❯} +} + +prompt_pure_update_vim_prompt_widget() { + setopt localoptions noshwordsplit + prompt_pure_state[prompt]=${${KEYMAP/vicmd/${PURE_PROMPT_VICMD_SYMBOL:-❮}}/(main|viins)/${PURE_PROMPT_SYMBOL:-❯}} + zle && zle .reset-prompt +} + +prompt_pure_reset_vim_prompt_widget() { + setopt localoptions noshwordsplit + prompt_pure_reset_prompt_symbol + zle && zle .reset-prompt +} + +prompt_pure_state_setup() { + setopt localoptions noshwordsplit + + # Check SSH_CONNECTION and the current state. + local ssh_connection=${SSH_CONNECTION:-$PROMPT_PURE_SSH_CONNECTION} + local username + if [[ -z $ssh_connection ]] && (( $+commands[who] )); then + # When changing user on a remote system, the $SSH_CONNECTION + # environment variable can be lost, attempt detection via who. + local who_out + who_out=$(who -m 2>/dev/null) + if (( $? )); then + # Who am I not supported, fallback to plain who. + who_out=$(who 2>/dev/null | grep ${TTY#/dev/}) + fi + + local reIPv6='(([0-9a-fA-F]+:)|:){2,}[0-9a-fA-F]+' # Simplified, only checks partial pattern. + local reIPv4='([0-9]{1,3}\.){3}[0-9]+' # Simplified, allows invalid ranges. + # Here we assume two non-consecutive periods represents a + # hostname. This matches foo.bar.baz, but not foo.bar. + local reHostname='([.][^. ]+){2}' + + # Usually the remote address is surrounded by parenthesis, but + # not on all systems (e.g. busybox). + local -H MATCH MBEGIN MEND + if [[ $who_out =~ "\(?($reIPv4|$reIPv6|$reHostname)\)?\$" ]]; then + ssh_connection=$MATCH + + # Export variable to allow detection propagation inside + # shells spawned by this one (e.g. tmux does not always + # inherit the same tty, which breaks detection). + export PROMPT_PURE_SSH_CONNECTION=$ssh_connection + fi + unset MATCH MBEGIN MEND + fi + + # show username@host if logged in through SSH + [[ -n $ssh_connection ]] && username='%F{242}%n@%m%f' + + # show username@host if root, with username in white + [[ $UID -eq 0 ]] && username='%F{white}%n%f%F{242}@%m%f' + + typeset -gA prompt_pure_state + prompt_pure_state=( + username "$username" + prompt "${PURE_PROMPT_SYMBOL:-❯}" + ) +} + +prompt_pure_setup() { + # Prevent percentage showing up if output doesn't end with a newline. + export PROMPT_EOL_MARK='' + + prompt_opts=(subst percent) + + # borrowed from promptinit, sets the prompt options in case pure was not + # initialized via promptinit. + setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}" + + if [[ -z $prompt_newline ]]; then + # This variable needs to be set, usually set by promptinit. + typeset -g prompt_newline=$'\n%{\r%}' + fi + + zmodload zsh/datetime + zmodload zsh/zle + zmodload zsh/parameter + + autoload -Uz add-zsh-hook + autoload -Uz vcs_info + autoload -Uz async && async + + # The add-zle-hook-widget function is not guaranteed + # to be available, it was added in Zsh 5.3. + autoload -Uz +X add-zle-hook-widget 2>/dev/null + + add-zsh-hook precmd prompt_pure_precmd + add-zsh-hook preexec prompt_pure_preexec + + prompt_pure_state_setup + + zle -N prompt_pure_update_vim_prompt_widget + zle -N prompt_pure_reset_vim_prompt_widget + if (( $+functions[add-zle-hook-widget] )); then + add-zle-hook-widget zle-line-finish prompt_pure_reset_vim_prompt_widget + add-zle-hook-widget zle-keymap-select prompt_pure_update_vim_prompt_widget + fi + + # if a virtualenv is activated, display it in grey + PROMPT='%(12V.%F{242}%12v%f .)' + + # prompt turns red if the previous command didn't exit with 0 + PROMPT+='%(?.%F{magenta}.%F{red})${prompt_pure_state[prompt]}%f ' + + # Store prompt expansion symbols for in-place expansion via (%). For + # some reason it does not work without storing them in a variable first. + typeset -ga prompt_pure_debug_depth + prompt_pure_debug_depth=('%e' '%N' '%x') + + # Compare is used to check if %N equals %x. When they differ, the main + # prompt is used to allow displaying both file name and function. When + # they match, we use the secondary prompt to avoid displaying duplicate + # information. + local -A ps4_parts + ps4_parts=( + depth '%F{yellow}${(l:${(%)prompt_pure_debug_depth[1]}::+:)}%f' + compare '${${(%)prompt_pure_debug_depth[2]}:#${(%)prompt_pure_debug_depth[3]}}' + 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' + secondary '%F{blue}%N%f%F{242}:%i' + prompt '%F{242}>%f ' + ) + # Combine the parts with conditional logic. First the `:+` operator is + # used to replace `compare` either with `main` or an ampty string. Then + # the `:-` operator is used so that if `compare` becomes an empty + # string, it is replaced with `secondary`. + local ps4_symbols='${${'${ps4_parts[compare]}':+"'${ps4_parts[main]}'"}:-"'${ps4_parts[secondary]}'"}' + + # Improve the debug prompt (PS4), show depth by repeating the +-sign and + # add colors to highlight essential parts like file and function name. + PROMPT4="${ps4_parts[depth]} ${ps4_symbols}${ps4_parts[prompt]}" + + unset ZSH_THEME # Guard against Oh My Zsh themes overriding Pure. +} + +prompt_pure_setup "$@" diff --git a/dot_local/share/toasty/prompts/prompt_purer_setup b/dot_local/share/toasty/prompts/prompt_purer_setup new file mode 100644 index 0000000..f800176 --- /dev/null +++ b/dot_local/share/toasty/prompts/prompt_purer_setup @@ -0,0 +1,404 @@ +# Pure +# by Sindre Sorhus +# https://github.com/sindresorhus/pure +# MIT License + +# For my own and others sanity +# git: +# %b => current branch +# %a => current action (rebase/merge) +# prompt: +# %F => color dict +# %f => reset color +# %~ => current path +# %* => time +# %n => username +# %m => shortname host +# %(?..) => prompt conditional - %(condition.true.false) +# terminal codes: +# \e7 => save cursor position +# \e[2A => move cursor 2 lines up +# \e[1G => go to position 1 in terminal +# \e8 => restore cursor position +# \e[K => clears everything after the cursor on the current line +# \e[2K => clear everything on the current line + +PURER_PROMPT_COMMAND_COUNT=0 +STATUS_COLOR='cyan' + +# turns seconds into human readable time +# 165392 => 1d 21h 56m 32s +# https://github.com/sindresorhus/pretty-time-zsh +prompt_pure_human_time_to_var() { + local human=" [" total_seconds=$1 var=$2 + local days=$(( total_seconds / 60 / 60 / 24 )) + local hours=$(( total_seconds / 60 / 60 % 24 )) + local minutes=$(( total_seconds / 60 % 60 )) + local seconds=$(( total_seconds % 60 )) + (( days > 0 )) && human+="${days}d " + (( hours > 0 )) && human+="${hours}h " + (( minutes > 0 )) && human+="${minutes}m " + human+="${seconds}s]" + + # store human readable time in variable as specified by caller + typeset -g "${var}"="${human}" +} + +# stores (into prompt_pure_cmd_exec_time) the exec time of the last command if set threshold was exceeded +prompt_pure_check_cmd_exec_time() { + integer elapsed + (( elapsed = EPOCHSECONDS - ${prompt_pure_cmd_timestamp:-$EPOCHSECONDS} )) + prompt_pure_cmd_exec_time= + (( elapsed > ${PURE_CMD_MAX_EXEC_TIME:=5} )) && { + prompt_pure_human_time_to_var $elapsed "prompt_pure_cmd_exec_time" + } +} + +prompt_pure_clear_screen() { + # enable output to terminal + zle -I + # clear screen and move cursor to (0, 0) + print -n '\e[2J\e[0;0H' + # reset command count to zero so we don't start with a blank line + PURER_PROMPT_COMMAND_COUNT=0 + # print preprompt + prompt_pure_preprompt_render precmd +} + +# set STATUS_COLOR: cyan for "insert", green for "normal" mode. +prompt_purer_vim_mode() { + STATUS_COLOR="${${KEYMAP/vicmd/green}/(main|viins)/cyan}" + prompt_pure_preprompt_render +} + +prompt_pure_set_title() { + # emacs terminal does not support settings the title + (( ${+EMACS} )) && return + + # tell the terminal we are setting the title + print -n '\e]0;' + # show hostname if connected through ssh + [[ -n $SSH_CONNECTION ]] && print -Pn '(%m) ' + case $1 in + expand-prompt) + print -Pn $2;; + ignore-escape) + print -rn $2;; + esac + # end set title + print -n '\a' +} + +prompt_pure_preexec() { + # attempt to detect and prevent prompt_pure_async_git_fetch from interfering with user initiated git or hub fetch + [[ $2 =~ (git|hub)\ .*(pull|fetch) ]] && async_flush_jobs 'prompt_pure' + + prompt_pure_cmd_timestamp=$EPOCHSECONDS + + # shows the current dir and executed command in the title while a process is active + prompt_pure_set_title 'ignore-escape' "$PWD:t: $2" + + # Disallow python virtualenv from updating the prompt, set it to 12 if + # untouched by the user to indicate that Pure modified it. Here we use + # magic number 12, same as in psvar. + export VIRTUAL_ENV_DISABLE_PROMPT=${VIRTUAL_ENV_DISABLE_PROMPT:-12} +} + +# string length ignoring ansi escapes +prompt_pure_string_length_to_var() { + local str=$1 var=$2 length + # perform expansion on str and check length + length=$(( ${#${(S%%)str//(\%([KF1]|)\{*\}|\%[Bbkf])}} )) + + # store string length in variable as specified by caller + typeset -g "${var}"="${length}" +} + +prompt_pure_preprompt_render() { + # store the current prompt_subst setting so that it can be restored later + local prompt_subst_status=$options[prompt_subst] + + # make sure prompt_subst is unset to prevent parameter expansion in preprompt + setopt local_options no_prompt_subst + + # check that no command is currently running, the preprompt will otherwise be rendered in the wrong place + [[ -n ${prompt_pure_cmd_timestamp+x} && "$1" != "precmd" ]] && return + + # set color for git branch/dirty status, change color if dirty checking has been delayed + local git_color=242 + [[ -n ${prompt_pure_git_last_dirty_check_timestamp+x} ]] && git_color=red + + # construct preprompt + local preprompt="" + + + # add a newline between commands + FIRST_COMMAND_THRESHOLD=1 + if [[ "$PURER_PROMPT_COMMAND_COUNT" -gt "$FIRST_COMMAND_THRESHOLD" ]]; then + preprompt+=$'\n' + fi + + local symbol_color="%(?.${PURE_PROMPT_SYMBOL_COLOR:-magenta}.red)" + + # show virtual env + preprompt+="%(12V.%F{242}%12v%f .)" + # begin with symbol, colored by previous command exit code + preprompt+="%F{$symbol_color}${PURE_PROMPT_SYMBOL:-❯}%f " + # directory, colored by vim status + preprompt+="%B%F{$STATUS_COLOR}%c%f%b" + # git info + preprompt+="%F{$git_color}${vcs_info_msg_0_}${prompt_pure_git_dirty}%f" + # git pull/push arrows + preprompt+="%F{cyan}${prompt_pure_git_arrows}%f" + # username and machine if applicable + preprompt+=$prompt_pure_username + # execution time + preprompt+="%B%F{242}${prompt_pure_cmd_exec_time}%f%b" + + preprompt+=" " + + # make sure prompt_pure_last_preprompt is a global array + typeset -g -a prompt_pure_last_preprompt + + PROMPT="$preprompt" + + # if executing through precmd, do not perform fancy terminal editing + if [[ "$1" != "precmd" ]]; then + # only redraw if the expanded preprompt has changed + # [[ "${prompt_pure_last_preprompt[2]}" != "${(S%%)preprompt}" ]] || return + + # redraw prompt (also resets cursor position) + zle && zle .reset-prompt + + setopt no_prompt_subst + fi + + # store both unexpanded and expanded preprompt for comparison + prompt_pure_last_preprompt=("$preprompt" "${(S%%)preprompt}") +} + +prompt_pure_precmd() { + # check exec time and store it in a variable + prompt_pure_check_cmd_exec_time + + # by making sure that prompt_pure_cmd_timestamp is defined here the async functions are prevented from interfering + # with the initial preprompt rendering + prompt_pure_cmd_timestamp= + + # shows the full path in the title + prompt_pure_set_title 'expand-prompt' '%~' + + # get vcs info + vcs_info + + # preform async git dirty check and fetch + prompt_pure_async_tasks + + # Check if we should display the virtual env, we use a sufficiently high + # index of psvar (12) here to avoid collisions with user defined entries. + psvar[12]= + # When VIRTUAL_ENV_DISABLE_PROMPT is empty, it was unset by the user and + # Pure should take back control. + if [[ -n $VIRTUAL_ENV ]] && [[ -z $VIRTUAL_ENV_DISABLE_PROMPT || $VIRTUAL_ENV_DISABLE_PROMPT = 12 ]]; then + psvar[12]="${VIRTUAL_ENV:t}" + export VIRTUAL_ENV_DISABLE_PROMPT=12 + fi + + # print the preprompt + prompt_pure_preprompt_render "precmd" + + # Increment command counter + PURER_PROMPT_COMMAND_COUNT=$((PURER_PROMPT_COMMAND_COUNT+1)) + + # print the preprompt + prompt_pure_preprompt_render "precmd" + + # remove the prompt_pure_cmd_timestamp, indicating that precmd has completed + unset prompt_pure_cmd_timestamp +} + +# fastest possible way to check if repo is dirty +prompt_pure_async_git_dirty() { + setopt localoptions noshwordsplit + local untracked_dirty=$1 dir=$2 + + # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks + builtin cd -q $dir + + if [[ $untracked_dirty = 0 ]]; then + command git diff --no-ext-diff --quiet --exit-code + else + test -z "$(command git status --porcelain --ignore-submodules -unormal)" + fi + + return $? +} + +prompt_pure_async_git_fetch() { + setopt localoptions noshwordsplit + # use cd -q to avoid side effects of changing directory, e.g. chpwd hooks + builtin cd -q $1 + + # set GIT_TERMINAL_PROMPT=0 to disable auth prompting for git fetch (git 2.3+) + export GIT_TERMINAL_PROMPT=0 + # set ssh BachMode to disable all interactive ssh password prompting + export GIT_SSH_COMMAND=${GIT_SSH_COMMAND:-"ssh -o BatchMode=yes"} + + command git -c gc.auto=0 fetch &>/dev/null || return 1 + + # check arrow status after a successful git fetch + prompt_pure_async_git_arrows $1 +} + +prompt_pure_async_git_arrows() { + setopt localoptions noshwordsplit + builtin cd -q $1 + command git rev-list --left-right --count HEAD...@'{u}' +} + +prompt_pure_async_tasks() { + setopt localoptions noshwordsplit + + # initialize async worker + ((!${prompt_pure_async_init:-0})) && { + async_start_worker "prompt_pure" -u -n + async_register_callback "prompt_pure" prompt_pure_async_callback + prompt_pure_async_init=1 + } + + # store working_tree without the "x" prefix + local working_tree="${vcs_info_msg_1_#x}" + + # check if the working tree changed (prompt_pure_current_working_tree is prefixed by "x") + if [[ ${prompt_pure_current_working_tree#x} != $working_tree ]]; then + # stop any running async jobs + async_flush_jobs "prompt_pure" + + # reset git preprompt variables, switching working tree + unset prompt_pure_git_dirty + unset prompt_pure_git_last_dirty_check_timestamp + prompt_pure_git_arrows= + + # set the new working tree and prefix with "x" to prevent the creation of a named path by AUTO_NAME_DIRS + prompt_pure_current_working_tree="x${working_tree}" + fi + + # only perform tasks inside git working tree + [[ -n $working_tree ]] || return + + async_job "prompt_pure" prompt_pure_async_git_arrows $working_tree + + # do not preform git fetch if it is disabled or working_tree == HOME + if (( ${PURE_GIT_PULL:-1} )) && [[ $working_tree != $HOME ]]; then + # tell worker to do a git fetch + async_job "prompt_pure" prompt_pure_async_git_fetch $working_tree + fi + + # if dirty checking is sufficiently fast, tell worker to check it again, or wait for timeout + integer time_since_last_dirty_check=$(( EPOCHSECONDS - ${prompt_pure_git_last_dirty_check_timestamp:-0} )) + if (( time_since_last_dirty_check > ${PURE_GIT_DELAY_DIRTY_CHECK:-1800} )); then + unset prompt_pure_git_last_dirty_check_timestamp + # check check if there is anything to pull + async_job "prompt_pure" prompt_pure_async_git_dirty ${PURE_GIT_UNTRACKED_DIRTY:-1} $working_tree + fi +} + +prompt_pure_check_git_arrows() { + setopt localoptions noshwordsplit + local arrows left=${1:-0} right=${2:-0} + + (( right > 0 )) && arrows+=${PURE_GIT_DOWN_ARROW:-⇣} + (( left > 0 )) && arrows+=${PURE_GIT_UP_ARROW:-⇡} + + [[ -n $arrows ]] || return + typeset -g REPLY=" $arrows" +} + +prompt_pure_async_callback() { + setopt localoptions noshwordsplit + local job=$1 code=$2 output=$3 exec_time=$4 + + case $job in + prompt_pure_async_git_dirty) + local prev_dirty=$prompt_pure_git_dirty + if (( code == 0 )); then + prompt_pure_git_dirty= + else + prompt_pure_git_dirty="*" + fi + + [[ $prev_dirty != $prompt_pure_git_dirty ]] && prompt_pure_preprompt_render + + # When prompt_pure_git_last_dirty_check_timestamp is set, the git info is displayed in a different color. + # To distinguish between a "fresh" and a "cached" result, the preprompt is rendered before setting this + # variable. Thus, only upon next rendering of the preprompt will the result appear in a different color. + (( $exec_time > 2 )) && prompt_pure_git_last_dirty_check_timestamp=$EPOCHSECONDS + ;; + prompt_pure_async_git_fetch|prompt_pure_async_git_arrows) + # prompt_pure_async_git_fetch executes prompt_pure_async_git_arrows + # after a successful fetch. + if (( code == 0 )); then + local REPLY + prompt_pure_check_git_arrows ${(ps:\t:)output} + if [[ $prompt_pure_git_arrows != $REPLY ]]; then + prompt_pure_git_arrows=$REPLY + prompt_pure_preprompt_render + fi + fi + ;; + esac +} + +prompt_pure_setup() { + # prevent percentage showing up + # if output doesn't end with a newline + export PROMPT_EOL_MARK='' + + # prompt_opts=(subst percent) + + # borrowed from promptinit, sets the prompt options in case pure was not + # initialized via promptinit. + # setopt noprompt{bang,cr,percent,subst} "prompt${^prompt_opts[@]}" + + zmodload zsh/datetime + zmodload zsh/zle + zmodload zsh/parameter + + autoload -Uz add-zsh-hook + autoload -Uz vcs_info + autoload -Uz async && async + + add-zsh-hook precmd prompt_pure_precmd + add-zsh-hook preexec prompt_pure_preexec + + zstyle ':vcs_info:*' enable git + zstyle ':vcs_info:*' use-simple true + # only export two msg variables from vcs_info + zstyle ':vcs_info:*' max-exports 2 + # vcs_info_msg_0_ = ' %b' (for branch) + # vcs_info_msg_1_ = 'x%R' git top level (%R), x-prefix prevents creation of a named path (AUTO_NAME_DIRS) + zstyle ':vcs_info:git*' formats ' %b' 'x%R' + zstyle ':vcs_info:git*' actionformats ' %b|%a' 'x%R' + + # if the user has not registered a custom zle widget for clear-screen, + # override the builtin one so that the preprompt is displayed correctly when + # ^L is issued. + if [[ $widgets[clear-screen] == 'builtin' ]]; then + zle -N clear-screen prompt_pure_clear_screen + fi + + # register custom function for vim-mode + zle -N zle-line-init prompt_purer_vim_mode + zle -N zle-keymap-select prompt_purer_vim_mode + + # show username@host if logged in through SSH + [[ "$SSH_CONNECTION" != '' ]] && prompt_pure_username=' %F{242}%n@%m%f' + + # show username@host if root, with username in white + [[ $UID -eq 0 ]] && prompt_pure_username=' %F{white}%n%f%F{242}@%m%f' + + # create prompt + prompt_pure_preprompt_render 'precmd' +} + +prompt_pure_setup "$@" diff --git a/dot_local/share/toasty/prompts/prompt_shellder_setup b/dot_local/share/toasty/prompts/prompt_shellder_setup new file mode 100644 index 0000000..671eac4 --- /dev/null +++ b/dot_local/share/toasty/prompts/prompt_shellder_setup @@ -0,0 +1,216 @@ +# vim:ft=zsh ts=2 sw=2 sts=2 + +# +# Segment drawing +# +CURRENT_BG='NONE' + +# +# color scheme +# +SHELLDER_CONTEXT_BG=${SHELLDER_CONTEXT_BG:-238} +SHELLDER_CONTEXT_FG=${SHELLDER_CONTEXT_FG:-250} + +SHELLDER_DIRECTORY_BG=${SHELLDER_DIRECTORY_BG:-234} +SHELLDER_DIRECTORY_FG=${SHELLDER_DIRECTORY_FG:-231} + +SHELLDER_GIT_CLEAN_BG=${SHELLDER_GIT_CLEAN_BG:-'green'} +SHELLDER_GIT_CLEAN_FG=${SHELLDER_GIT_CLEAN_FG:-'black'} +SHELLDER_GIT_DIRTY_BG=${SHELLDER_GIT_DIRTY_BG:-202} +SHELLDER_GIT_DIRTY_FG=${SHELLDER_GIT_DIRTY_FG:-'black'} + +SHELLDER_VIRTUALENV_BG=${SHELLDER_VIRTUALENV_BG:-'blue'} +SHELLDER_VIRTUALENV_FG=${SHELLDER_VIRTUALENV_FG:-'black'} + +SHELLDER_STATUS_BG=${SHELLDER_STATUS_BG:-'black'} +SHELLDER_STATUS_FG=${SHELLDER_STATUS_FG:-'default'} + +# Special Powerline characters +() { + local LC_ALL="" LC_CTYPE="en_US.UTF-8" + SEGMENT_SEPARATOR=$'\ue0b0' +} + +# Begin a segment +# Takes two arguments, background and foreground. Both can be omitted, +# rendering default background/foreground. +prompt_segment() { + local bg fg + [[ -n $1 ]] && bg="%K{$1}" || bg="%k" + [[ -n $2 ]] && fg="%F{$2}" || fg="%f" + if [[ $CURRENT_BG != 'NONE' && $1 != $CURRENT_BG ]]; then + echo -n " %{$bg%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR%{$fg%} " + else + echo -n "%{$bg%}%{$fg%} " + fi + CURRENT_BG=$1 + [[ -n $3 ]] && echo -n $3 +} + +# End the prompt, closing any open segments +prompt_end() { + if [[ -n $CURRENT_BG ]]; then + echo -n " %{%k%F{$CURRENT_BG}%}$SEGMENT_SEPARATOR" + else + echo -n "%{%k%}" + fi + echo -n "%{%f%}" + CURRENT_BG='' +} + + +# +# Prompt functions +# + +# Context: user@hostname (who am I and where am I) +prompt_context() { + local prompt + if [[ "$USER" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then + if [[ "$USER" != "$DEFAULT_USER" ]]; then + prompt="%(!.%{%F{yellow}%}.)$USER@%m" + else + prompt="%(!.%{%F{yellow}%}.)%m" + fi + prompt_segment $SHELLDER_CONTEXT_BG $SHELLDER_CONTEXT_FG $prompt + fi +} + +# Git: branch/detached head, dirty status +prompt_git() { + local repo_path + repo_path=$(git rev-parse --git-dir 2>/dev/null) + + if [[ -n $repo_path ]]; then + local PL_BRANCH_CHAR dirty bgcolor fgcolor mode ref + + () { + local LC_ALL="" LC_CTYPE="en_US.UTF-8" + PL_BRANCH_CHAR=$'\ue0a0' # + } + + dirty=$(command git status --porcelain --ignore-submodules=dirty 2> /dev/null) + if [[ -n $dirty ]]; then + if [[ -z $MSYS ]]; then + bgcolor='yellow' + fgcolor='black' + else + bgcolor=$SHELLDER_GIT_DIRTY_BG # vcs_info will be disabled with MSYS2, warn it with color + fgcolor=$SHELLDER_GIT_DIRTY_FG + fi + else + bgcolor=$SHELLDER_GIT_CLEAN_BG + fgcolor=$SHELLDER_GIT_CLEAN_FG + fi + prompt_segment $bgcolor $fgcolor + + if [[ -e "${repo_path}/BISECT_LOG" ]]; then + mode=" <B>" + elif [[ -e "${repo_path}/MERGE_HEAD" ]]; then + mode=" >M<" + elif [[ -e "${repo_path}/rebase" || -e "${repo_path}/rebase-apply" || -e "${repo_path}/rebase-merge" || -e "${repo_path}/../.dotest" ]]; then + mode=" >R>" + fi + + # vcs_info is too slow with MSYS2 (~300ms with i7-6770K + SSD) + if [[ -z $MSYS ]]; then + autoload -Uz vcs_info + zstyle ':vcs_info:*' enable git + zstyle ':vcs_info:*' check-for-changes true + zstyle ':vcs_info:*' stagedstr '✚' + zstyle ':vcs_info:*' unstagedstr '●' + zstyle ':vcs_info:*' formats ' %u%c' + zstyle ':vcs_info:*' actionformats ' %u%c' + vcs_info + else + if [[ -n $dirty ]]; then + vcs_info_msg_0_=' !' + fi + fi + + ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(git rev-parse --short HEAD 2> /dev/null)" + echo -n "${ref/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}" + fi +} + +prompt_hg() { + local rev status + if $(hg id >/dev/null 2>&1); then + if $(hg prompt >/dev/null 2>&1); then + if [[ $(hg prompt "{status|unknown}") = "?" ]]; then + # if files are not added + prompt_segment red white + st='±' + elif [[ -n $(hg prompt "{status|modified}") ]]; then + # if any modification + prompt_segment yellow black + st='±' + else + # if working copy is clean + prompt_segment green black + fi + echo -n $(hg prompt "☿ {rev}@{branch}") $st + else + st="" + rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g') + branch=$(hg id -b 2>/dev/null) + if `hg st | grep -q "^\?"`; then + prompt_segment red black + st='±' + elif `hg st | grep -q "^[MA]"`; then + prompt_segment yellow black + st='±' + else + prompt_segment green black + fi + echo -n "☿ $rev@$branch" $st + fi + fi +} + +# Dir: current working directory +prompt_dir() { + local dir + if (( $+functions[shrink_path] )); then + dir=$(shrink_path -f) + else + dir='%~' + fi + prompt_segment $SHELLDER_DIRECTORY_BG $SHELLDER_DIRECTORY_FG $dir +} + +# Virtualenv: current working virtualenv +prompt_virtualenv() { + local virtualenv_path="$VIRTUAL_ENV" + if [[ -n $virtualenv_path && -n $VIRTUAL_ENV_DISABLE_PROMPT ]]; then + prompt_segment $SHELLDER_VIRTUALENV_BG $SHELLDER_VIRTUALENV_FG "(`basename $virtualenv_path`)" + fi +} + +# Status: error + root + background jobs +prompt_status() { + local symbols + symbols=() + [[ $RETVAL -ne 0 ]] && symbols+="%{%F{red}%}✘" + [[ $UID -eq 0 ]] && symbols+="%{%F{yellow}%}⚡" + [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="%{%F{cyan}%}⚙" + + [[ -n "$symbols" ]] && prompt_segment $SHELLDER_STATUS_BG $SHELLDER_STATUS_FG "$symbols" +} + + +# +# Prompt +# +build_prompt() { + RETVAL=$? + prompt_status + prompt_virtualenv + prompt_context + prompt_dir + prompt_git + prompt_hg + prompt_end +} +setopt prompt_subst +PROMPT='%{%f%b%k%}$(build_prompt) ' diff --git a/dot_local/share/toasty/prompts/prompt_toasty_setup b/dot_local/share/toasty/prompts/prompt_toasty_setup new file mode 100644 index 0000000..c10c766 --- /dev/null +++ b/dot_local/share/toasty/prompts/prompt_toasty_setup @@ -0,0 +1,32 @@ +setopt promptsubst + +autoload -Uz add-zsh-hook +autoload -Uz vcs_info + +zstyle ':vcs_info:*' enable git +zstyle ':vcs_info:*' check-for-changes false +zstyle ':vcs_info:*' actionformats \ + '%B%F{cyan}(%b)%f%%b%c' +zstyle ':vcs_info:*' formats \ + '%B%F{cyan}(%b)%f%%b%c' + +# we want to catch unstaged and untracked in one swoop +zstyle ':vcs_info:git*+set-message:*' hooks git-dirty +function +vi-git-dirty { + if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] + then + if git status --porcelain | grep -Fq -e 'A' -e '??' -e 'M' + then + hook_com[staged]+=' %B%F{yellow}✗%f%b ' + else + hook_com[staged]+=' %B%F{green}✔%f%b ' + fi + fi +} + +add-zsh-hook -Uz precmd vcs_info + +PS1='%B%(?.%F{green}.%F{red})→%f%b %B%(!.%F{red}.%F{blue})%1~%f%b ${vcs_info_msg_0_}' +RPS1='%B%F{red}[%n@%m]%f%b' + +# vim: ft=zsh diff --git a/dot_local/share/toasty/source/aliases.zsh b/dot_local/share/toasty/source/aliases.zsh new file mode 100644 index 0000000..69b9c24 --- /dev/null +++ b/dot_local/share/toasty/source/aliases.zsh @@ -0,0 +1,4 @@ +# Various common aliases + +alias grep='grep --color=auto' +alias ls='ls --color=auto' ll='ls -lh' l=ll la='l -a' diff --git a/dot_local/share/toasty/source/bindkeys.zsh b/dot_local/share/toasty/source/bindkeys.zsh new file mode 100644 index 0000000..0c78646 --- /dev/null +++ b/dot_local/share/toasty/source/bindkeys.zsh @@ -0,0 +1,36 @@ +# this is totally not shamelessly stolen from omz-keybindings and zshwiki +# definitely not +# no way + +# basic bindings +bindkey ${terminfo[khome]} beginning-of-line +bindkey ${terminfo[kend]} end-of-line + +bindkey ${terminfo[kdch1]} delete-char + +bindkey ${terminfo[kcuu1]} up-line-or-history +bindkey ${terminfo[kcud1]} down-line-or-history + +bindkey ${terminfo[kcub1]} backward-char +bindkey ${terminfo[kcuf1]} forward-char + +bindkey '^?' backward-delete-char +bindkey ${terminfo[kdch1]} delete-char + +# opinionated bindings +bindkey '^r' history-incremental-search-backward +bindkey '^[[1;5C' forward-word +bindkey '^[[1;5D' backward-word +bindkey ${terminfo[kcbt]} reverse-menu-complete + +# make sure terminfo keys make actual sense +if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then + function zle-line-init () { + echoti smkx + } + function zle-line-finish () { + echoti rmkx + } + zle -N zle-line-init + zle -N zle-line-finish +fi diff --git a/dot_local/share/toasty/source/completions.zsh b/dot_local/share/toasty/source/completions.zsh new file mode 100644 index 0000000..8c3f0ae --- /dev/null +++ b/dot_local/share/toasty/source/completions.zsh @@ -0,0 +1,4 @@ +zstyle ':completion:*' list-colors "${(@s.:.)LS_COLORS}" + +zstyle ':completion*:cd:*' tag-order local-directories directory-stack path-directories +zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*' diff --git a/dot_local/share/toasty/source/env.zsh b/dot_local/share/toasty/source/env.zsh new file mode 100644 index 0000000..0da30e0 --- /dev/null +++ b/dot_local/share/toasty/source/env.zsh @@ -0,0 +1,9 @@ +# various environment variables that should generally make sense + +# primarily for embedded environments with busybox etc, avoid nano. do override +EDITOR=vi + +PAGER=less +READNULLCMD=less + +export EDITOR diff --git a/dot_local/share/toasty/source/options.zsh b/dot_local/share/toasty/source/options.zsh new file mode 100644 index 0000000..0f6d0f6 --- /dev/null +++ b/dot_local/share/toasty/source/options.zsh @@ -0,0 +1,36 @@ +# changing directories +setopt autocd +setopt noautopushd +setopt pushdignoredups +setopt pushdtohome + +# completion +setopt alwaystoend + +# expansion and globbing + +# history +setopt histexpiredupsfirst +setopt histignoredups +setopt histignorespace +setopt histverify +setopt incappendhistory + +# initialization +setopt noallexport + +# i/o +setopt noflowcontrol +setopt interactivecomments + +# job control +setopt longlistjobs + +# prompting +setopt promptsubst +# setopt transientrprompt ? + +# scripts and functions + +# shell emulation +setopt bsdecho diff --git a/dot_local/share/toasty/third-party-licenses/LICENSE.OMZ.md b/dot_local/share/toasty/third-party-licenses/LICENSE.OMZ.md new file mode 100644 index 0000000..a8e750c --- /dev/null +++ b/dot_local/share/toasty/third-party-licenses/LICENSE.OMZ.md @@ -0,0 +1,24 @@ +Some of the items included here are directly taken from Oh-My-Zsh. This license applies to them. + +The MIT License (MIT) + +Copyright (c) 2009-2016 Robby Russell and contributors +See the full list at https://github.com/robbyrussell/oh-my-zsh/contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dot_local/share/toasty/third-party-licenses/LICENSE.pure.txt b/dot_local/share/toasty/third-party-licenses/LICENSE.pure.txt new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/dot_local/share/toasty/third-party-licenses/LICENSE.pure.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dot_local/share/toasty/third-party-licenses/LICENSE.purer.txt b/dot_local/share/toasty/third-party-licenses/LICENSE.purer.txt new file mode 100644 index 0000000..654d0bf --- /dev/null +++ b/dot_local/share/toasty/third-party-licenses/LICENSE.purer.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dot_local/share/toasty/third-party-licenses/LICENSE.shellder.txt b/dot_local/share/toasty/third-party-licenses/LICENSE.shellder.txt new file mode 100644 index 0000000..9a97054 --- /dev/null +++ b/dot_local/share/toasty/third-party-licenses/LICENSE.shellder.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2016 Hyeon Kim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dot_local/share/toasty/zshenv b/dot_local/share/toasty/zshenv new file mode 100644 index 0000000..6bfcdf7 --- /dev/null +++ b/dot_local/share/toasty/zshenv @@ -0,0 +1 @@ +emulate sh -c 'source /etc/profile' diff --git a/dot_local/share/toasty/zshrc b/dot_local/share/toasty/zshrc new file mode 100644 index 0000000..1fbe57b --- /dev/null +++ b/dot_local/share/toasty/zshrc @@ -0,0 +1,61 @@ +print -Pv zrc %N # get current file location, store it in $zrc +zrc=$zrc:A # resolve $zrc (assume path) to its absolute location +zshd=${zrc:h} + +: ${zd:=$HOME/.zsh} + +# spath -> autosource path, ala plugins +# apath -> sourceall path, for .d dirs +typeset -T SPATH spath +typeset -T APATH apath + +# default values +spath=( + $zd/plugins + $zshd/plugins +) +apath=( + $zshd/source + $zd/source +) + +# user stuff comes first +# completions come after the functions they complete +fpath+=( + $zd/functions + $zd/completions + $zd/prompts + $zshd/functions + $zshd/completions + $zshd/prompts +) +# you can use your functions as standalone scripts without autoloading them +# just +x +path+=( + $zd/functions +) + +# sourced before sourcealling +# should be the location to edit fpath/apath/spath +[[ -f $zd/pre ]] && . $zd/pre + +# allow digest drop-in +if [[ -d $zd/digests ]]; then + local f= + for f in $zd/digests/*.zwc(N); do + fpath+=( $f ) + autoload -w $f + done +fi + +autoload sourceall +sourceall zsh # source every .zsh file in every $apath[@] directory + +# local zshrc +[[ -f $zd/zshrc.local ]] && . $zd/zshrc.local + +# LITERALLY THE VERY LAST THING WE DO IS COMPINIT PLS DUN DO IT URSELF +autoload -Uz compinit +compinit + +# vim: ft=zsh |
