2 Commits

Author SHA1 Message Date
8fbb496b0d Add autoenv, automatic environment setup
Using a hook, automatically detect when `.enter` and `.exit` scripts
and source them when changing the shells current directory.
2018-04-22 12:14:29 +01:00
f19bf41c63 Improve & rename plugin sourcing function
Rename `plugin-load` to `source-plugin` and add a check for the plugin
directory, a message is displayed if it does not exist.
2018-04-21 17:21:00 +01:00
29 changed files with 319 additions and 1828 deletions

6
$
View File

@@ -1,6 +0,0 @@
#!/usr/bin/env zsh
read -p "Are you sure? [y/N]: " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
exec "$@"
fi

23
.conduit.yaml Normal file
View File

@@ -0,0 +1,23 @@
---
- location: ~/.config/zsh
- apt:
- zsh
- zsh-doc
- brew:
- zsh
- command:
- sudo chsh $USER -s `which zsh`
- symlink:
- {src: zlogin, dst: ~/.zlogin}
- {src: zlogout, dst: ~/.zlogout}
- {src: zprofile, dst: ~/.zprofile}
- {src: zshenv, dst: ~/.zshenv}
- {src: zshrc, dst: ~/.zshrc}
-
src: prompt_fresh_setup
dst: ~/.local/share/zsh/site-functions/prompt_fresh_setup
- repo:
- https://github.com/zsh-users/zsh-autosuggestions.git
- https://github.com/zsh-users/zsh-history-substring-search.git
- https://github.com/zsh-users/zsh-syntax-highlighting.git
- message: zsh will be the default prompt after next login

4
.gitignore vendored
View File

@@ -1,4 +1,2 @@
# Ignore all plugin files in subdirectories # Ignore all plugin files in subdirectories
zsh-*/ */*
local
zsh*.local

View File

@@ -2,13 +2,15 @@
<!-- TODO: GIF --> <!-- TODO: GIF -->
## Ansible ## [conduit][conduit]
To install this Zsh configuration: Simple, local, configuration manager.
```console Installation of the configuration files in this repository is orchestrated by
$ ansible-playbook main.yaml --ask-become-pass [conduit][conduit] as defined in the [`.conduit.yaml`](.conduit.yaml) file. This
``` includes [Zsh][zsh] package installs, setting [Zsh][zsh] as the users default
shell, and symbolic linking file such as [`zshrc`](zshrc) to `~/.zshrc`, and
cloning plugin repositories. The repository is cloned to `~/.config/zsh`.
## Prompt ## Prompt
@@ -49,25 +51,10 @@ display. This results in no noticeable lag when redrawing the prompt.
Plugins are sourced manually and their git repositories tracked by Plugins are sourced manually and their git repositories tracked by
[conduit][conduit], no plugin manager is used. [conduit][conduit], no plugin manager is used.
* [zsh-autosuggestions][zsh-autosuggestions] Fish-like autosuggestions for zsh.
* [zsh-syntax-highlighting][syntax] Fish shell like syntax highlighting for Zsh. * [zsh-syntax-highlighting][syntax] Fish shell like syntax highlighting for Zsh.
* [zsh-history-substring-search][search] Zsh port of the Fish shell's history * [zsh-history-substring-search][search] Zsh port of the Fish shell's history
search. search.
In addition to third party plugins the following are custom plugins residing in
this repository.
* [autoenv](autoenv/autoenv.zsh) is a inspired by [zsh-autoenv][zsh-autoenv] but
simplified by removing customization points and using a less intrusive UI.
* [build](build/build.plugin.zsh) is a collection of commands to make it easier
to build projects focuses on C/C++ development.
* [sandbox](sandbox/sandbox.plugin.zsh) is a command which sets up a throw away
directory for quickly testing ideas.
* [layout](layout/layout.plugin.zsh) is a command which setups up `tmux` panes
in a window with scripts.
* [notes](notes/notes.plugin.zsh) is a command to quickly edit markdown note
files.
## Environment & Settings ## Environment & Settings
The bulk of, if not all, configuration occurs in [`zshenv`](zshenv) and The bulk of, if not all, configuration occurs in [`zshenv`](zshenv) and
@@ -148,8 +135,6 @@ Various aliases are defined at the end of [zshrc](zshrc) for convenience.
[zsh]: https://www.zsh.org/ [zsh]: https://www.zsh.org/
[git]: https://git-scm.com/ [git]: https://git-scm.com/
[git-prompt]: https://github.com/olivierverdier/zsh-git-prompt [git-prompt]: https://github.com/olivierverdier/zsh-git-prompt
[zsh-autosuggestions]: https://github.com/zdharma/fast-syntax-highlighting
[zsh-autoenv]: https://github.com/Tarrasch/zsh-autoenv
[syntax]: https://github.com/zsh-users/zsh-syntax-highlighting [syntax]: https://github.com/zsh-users/zsh-syntax-highlighting
[search]: https://github.com/zsh-users/zsh-history-substring-search [search]: https://github.com/zsh-users/zsh-history-substring-search
[vim-mode]: https://github.com/sharat87/zsh-vim-mode [vim-mode]: https://github.com/sharat87/zsh-vim-mode

View File

@@ -1,27 +0,0 @@
#compdef autoenv
# Completion for the autoenv command.
_autoenv() {
local ret=1 context curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C -w -s \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'1: :->command'
case $state in
(command)
declare -a commands
local commands=(
init:'add .enter and .exit scripts in current directory'
edit:'edit .enter and .exit scripts in current directory'
deinit:'remove .enter and .exit scripts in current directory'
reload:'reload the current environment'
add=local:'add .local/bin to PATH'
add=py:'add Python virtualenv to the autoenv'
)
_describe -t commands command commands && ret=0 ;;
esac
return ret
}

View File

@@ -1,4 +1,73 @@
# Source the autoenv implementation script. # Automatically update the environment when the current working directory
source ${0:a:h}/autoenv.zsh # changes, this is a reimplementation of the ideas found in the repository
# Add the autoenv directory to fpath for completion. # https://github.com/Tarrasch/zsh-autoenv stripped down to suit my specific
fpath+=${0:a:h} # needs.
# Global entered directories array.
_autoenv_entered=()
# Source an enter script and add its directory to the global entered
# directories array.
_autoenv_enter() {
local entered=$1
# If entered has already been sourced, return.
(( ${+_autoenv_entered[${_autoenv_entered[(i)$entered]}]} )) && return
# If the enter script is not authorized, return.
# TODO: _autoenv_authorized $entered/.enter || return
# Source the enter script.
source $entered/.enter
# Add the entered directory to the global entered array.
_autoenv_entered+=$entered
}
# Source an exit script and remove its directory from the global entered
# directories array.
_autoenv_exit() {
local entered=$1
# If the exit script is not authorized, return.
# TODO: _autoenv_authorized $entered/.exit || return
# Source the exit script.
source $entered/.exit
# Remove the entered directory from the global entered array.
_autoenv_entered[${_autoenv_entered[(i)$entered]}]=()
}
# Find a directory containing a .enter file by searching up the directory tree
# starting in the current directory.
_autoenv_find_enter() {
local current=$PWD
# If an enter script is found in the current directory, return it.
[ -f "$current/.enter" ] && echo $current
# Loop until an enter script or the root directory is found.
while true; do
# Walk up one directory and make the path absolute.
local next=$current/..; local next=${next:A}
# If the current directory equals the next directory, return.
[[ $current == $next ]] && return || local current=$next
# If an enter script is found in the current directory, return it.
[ -f $current/.enter ] && echo $current
done
}
# A chpwd hook function which automatically sources enter and exit scripts to
# setup local environments for directory and its subdirectories.
_autoenv_chpwd() {
local entered
# Loop over the entered directory stack.
for entered in $_autoenv_entered; do
# If the the current directory was previously entered then exit.
# TODO: Verify what this condition expression actually does.
! [[ $PWD/ == $entered/* ]] && _autoenv_exit $entered
done
# Find the nearest enter script directory.
local enter=`_autoenv_find_enter`
# If the enter directory exists, enter it.
[ -d "$enter" ] && _autoenv_enter $enter
}
# Register the autoenv chpwd hook.
autoload -U add-zsh-hook
add-zsh-hook chpwd _autoenv_chpwd
# Ensure autoenv is activated in the current directory on first load.
_autoenv_chpwd

View File

@@ -1,245 +0,0 @@
# Automatically update the environment when the current working directory
# changes, this is a reimplementation of the ideas found in the repository
# https://github.com/Tarrasch/zsh-autoenv stripped down to bare essentials.
#
# The secret sauce can be found at the bottom of this file, where the chpwd
# hook function _autoenv_chpwd is added.
# The autoenv command provides a convenient way to create, edit, and remove
# enter and exit scripts in the current directory.
autoenv() {
local cmd=$1
case "$cmd" in
-h|--help) # Display help.
echo "\
usage: autoenv [-h] {init,edit,deinit,reload,add=py}
options:
-h, --help show this help message and exit
commands:
init add .enter and .exit scripts in current directory
edit edit .enter and .exit scripts in current directory
deinit remove .enter and .exit scripts in current directory
reload reload the current environment
add=local add .local/bin to PATH
add=py add Python virtualenv to the autoenv"
;;
init) # Create .enter and .exit scripts in current directory.
if [ -f $PWD/.enter ] || [ -f $PWD/.exit ]; then
echo '.enter or .exit already exists'; return 1
fi
# Create the .enter and .exit scripts.
touch .enter .exit
# If enter script exists, authorize it.
[ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes
# If exit script exists, authorize it.
[ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes
# Enter the autoenv.
_autoenv_enter $PWD
;;
edit) # Edit .enter and .exit scripts in current directory.
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
# Exit the autoenv before editing.
_autoenv_exit $PWD
if $EDITOR -p $PWD/.enter $PWD/.exit; then
# If enter script exists, authorize it.
[ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes
# If exit script exists, authorize it.
[ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes
fi
# Enter the autoenv.
_autoenv_enter $PWD
;;
deinit) # Remove .enter and .exit scripts in current directory.
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
# Prompt user to confirm removal of enter and exit scripts.
while true; do
read "answer?Are you sure [y/N]? "
case "$answer" in
y|Y|yes)
# Exit the autoenv.
_autoenv_exit $PWD
# Remove enter and exit scripts if they exist.
[ -f $PWD/.enter ] && rm $PWD/.enter
[ -f $PWD/.exit ] && rm $PWD/.exit
break ;;
*) break ;;
esac
done
;;
reload) # Reload the current environment
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
# Exit the autoenv before editing.
_autoenv_exit $PWD
# Enter the autoenv.
_autoenv_enter $PWD
;;
add=local) # Add .local/bin to PATH
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
_autoenv_exit $PWD
# Create .local/bin if not present
if ! [ -d $PWD/.local/bin ]; then
mkdir -p $PWD/.local/bin
fi
# On enter: store PATH and insert .local/bin
echo 'OLDPATH=$PATH' >> .enter
echo 'PATH=$PWD/.local/bin:$PATH' >> .enter
# On exit: reset PATH
echo 'PATH=$OLDPATH' >> .exit
echo 'unset OLDPATH' >> .exit
# Authorize modified autoenv
_autoenv_authorized $PWD/.enter yes
_autoenv_authorized $PWD/.exit yes
_autoenv_enter $PWD
;;
add=py) # Add Python virtualenv to the sandbox
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
_autoenv_exit $PWD
virtualenv -p `command -v python` .local
echo 'source ${0:a:h}/.local/bin/activate' >> .enter
echo 'deactivate' >> .exit
_autoenv_authorized $PWD/.enter yes
_autoenv_authorized $PWD/.exit yes
_autoenv_enter $PWD
pip install pynvim
;;
*) # Invalid arguments, show help then error.
echo "invalid arguments: $@"
autoenv --help
return 1
;;
esac
}
# Global entered directories array.
_autoenv_entered=()
# Load zstat from stat module for inspecting modified time.
zmodload -F zsh/stat b:zstat
# Check if the given file is authorized, if not prompt the user to authorize,
# ignore, or view the file. Authorized files and their modified times are
# stored in the $XDG_STATE_HOME/autoenv/authorized file to make authorization
# persistent.
_autoenv_authorized() {
local file=$1 yes=$2
# If autoenv state directory does not exist, create it.
! [ -d ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv ] && \
mkdir -p ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv
# Migrate from cache to state directory
[ -f $HOME/.cache/autoenv/authorized ] && \
mv $HOME/.cache/autoenv/authorized \
${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
# If the authorized file does not exist, create it.
! [ -f ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized ] && \
touch ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
# Load the authorized file into a map of authorized key value pairs.
typeset -A authorized=(`cat ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized`)
# If the file has been removed, return.
! [ -f $file ] && return 1
# If the given file has been authorized, i.e. the modified time matches that
# held in the authorized file, return.
local modified_time=`zstat +mtime $file`
[ "$authorized[$file]" = "$modified_time" ] && return
# If yes, don't prompt for user confirmation.
if [ "$yes" != "yes" ]; then
# Prompt to authorize file.
while true; do
read "answer?Authorize $file [Y/n/v]? "
case "$answer" in
y|Y|yes|'') break ;; # Authorize the file.
n|N|no) return 1 ;; # Do not authorize the file.
v|V|view) cat $file ;; # View the file.
esac
done
fi
# Add file to the authorized map.
authorized[$file]=$modified_time
# Store authorized map in authorized file.
echo ${(kv)authorized} > ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
}
# Source an enter script and add its directory to the global entered
# directories array.
_autoenv_enter() {
local entered=$1
# If entered exists in the entered directories array, return.
(( ${+_autoenv_entered[${_autoenv_entered[(i)$entered]}]} )) && return
# If the enter script is not authorized, return.
_autoenv_authorized $entered/.enter || return
# Source the enter script.
source $entered/.enter
# Add the entered directory to the global entered array.
_autoenv_entered+=$entered
}
# Source an exit script and remove its directory from the global entered
# directories array.
_autoenv_exit() {
local entered=$1
# If the exit script is not authorized, return.
_autoenv_authorized $entered/.exit || return
# Source the exit script.
source $entered/.exit
# Remove the entered directory from the global entered array.
_autoenv_entered[${_autoenv_entered[(i)$entered]}]=()
}
# Find all directories containing a .enter file by searching up the directory
# tree starting in the current directory.
_autoenv_find_enter_directories() {
local current=$PWD
# If an enter script is found in the current directory, return it.
[ -f $current/.enter ] && echo $current
# Loop until an enter script or the root directory is found.
while true; do
# Go up one directory and make the path absolute.
local next=$current/..; local next=${next:A}
# If an enter script is found in the current directory, return it.
[ -f $next/.enter ] && echo $next
# If the current directory equals the next directory we are done, otherwise
# update the current directory.
[[ $current == $next ]] && return || local current=$next
done
}
# A chpwd hook function which automatically sources enter and exit scripts to
# setup local environments for directory and its subdirectories.
_autoenv_chpwd() {
local entered
# Loop over the reversed entered directory array.
for entered in ${(aO)_autoenv_entered}; do
# If the the current directory was previously entered then exit.
! [[ $PWD/ == $entered/* ]] && _autoenv_exit $entered
done
# Find all enter script directories, store them in an array.
local enter_dirs=(`_autoenv_find_enter_directories`)
# Loop over reversed enter script directories array, so enter scripts found
# last are sourced first, then source all enter scripts.
for enter in ${(aO)enter_dirs}; do _autoenv_enter $enter; done
}
# Register the autoenv chpwd hook.
autoload -U add-zsh-hook
add-zsh-hook chpwd _autoenv_chpwd
# Ensure autoenv is activated in the current directory on first load.
_autoenv_chpwd

View File

@@ -1,7 +0,0 @@
#compdef build-dir
_arguments \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'(-s --show)'{-s,--show}'[show the current build directory]' \
'--build[invoke a build after selection]' \
'1:directory:_files'

View File

@@ -1,170 +0,0 @@
# A collection of commands to make it easier to build projects.
# Default `build` alias to select a `build-dir` then invoke a build, using an
# alias means the configured build command's completion works out of the box.
alias build="build-dir --build"
# Detect installed debugger and set the `debug` alias to debug a program with
# command line arguments.
if [ `uname` = Linux ]; then
autoload -U regexp-replace
function vimdebug() {
# For each item in $* replace * and \* and then replace \ with \\
local args=()
for arg in "$@"; do
regexp-replace arg '\*' '\\*'
args+=($arg)
done
nvim "+packadd termdebug" "+TermdebugCommand $args"
}
if command -v nvim &> /dev/null; then
alias debug=vimdebug
elif command -v gdb &> /dev/null; then
alias debug='gdb --args'
fi
elif [ `uname` = Darwin ]; then
command -v lldb &> /dev/null && \
alias debug='lldb --'
fi
# Interactively choose a `~build` directory for `build` to build.
build-dir() {
local usage='usage: build-dir [-h] [-s] [--build] [<directory>]'
local -a help show do_build
zparseopts -D h=help -help=help s=show -show=show -build=do_build
if [[ -n $help ]]; then
cat << EOF
$usage
Find and select the current build directory interactively.
positional arguments:
<directory> the build directory to select
optional arguments:
-h, --help show this help message and exit
-s, --show show the current build directory
--build invoke a build after selection
EOF
return
fi
error() { echo "\e[31merror:\e[0m $1" }
warning() { echo "\e[33mwarning:\e[0m $1" }
if [[ -n $show ]]; then
if [[ ! -n $build_dir ]]; then
error "build directory not set"
return 1
else
echo "$build_dir"
return
fi
fi
local local_build_dir
if [[ ${#*} -gt 1 ]]; then
echo $usage
error "unexpected positional arguments: ${*[2,${#*}]}"; return 1
elif [[ ${#*} -eq 1 ]]; then
if [[ ! -d ${*[1]} ]]; then
warning "directory not found: ${*[1]}"
else
local_build_dir=${*[1]}
fi
fi
# If <directory> was not set begin selection
if [[ -z $local_build_dir ]]; then
# Find build directories
local -a local_build_dirs
for entry in `ls -A`; do
[ -d $entry ] && [[ $entry =~ build* ]] && \
local_build_dirs+=${entry/\//}
done
# Interactively select a build directory if more than 1 found
integer index=0
if [[ ${#local_build_dirs} -eq 0 ]]; then
error "no build directories found"; return 1
elif [[ ${#local_build_dirs} -eq 1 ]]; then
local_build_dir=${local_build_dirs[1]}
elif [[ ${#local_build_dirs} -gt 1 ]]; then
# Use fzf to select a build directory
local max=$(( $( tput lines ) / 2 ))
local best=$(( ${#local_build_dirs} + 4 ))
local_build_dir=$(
printf '%s\n' "${local_build_dirs[@]}" |
fzf --layout=reverse --tac --info=hidden --border=rounded \
--cycle --height=$(( $best < $max ? $best : $max ))
)
if [[ $? -ne 0 ]]; then
return 1
fi
fi
fi
# If `build.ninja` exists in alias `ninja`, return.
local build
[ -f $local_build_dir/build.ninja ] && \
build="ninja -C $local_build_dir"
# If `Makefile` exists in alias `make`, return.
if [ -f $local_build_dir/Makefile ]; then
[ `uname` = Darwin ] && \
local cpu_count=`sysctl -n hw.ncpu` ||
local cpu_count=`grep -c '^processor' /proc/cpuinfo`
build="make -j $cpu_count -C $local_build_dir"
fi
# If the build variable is not defined the command could not be determined
if [ -z $build ]; then
warning "build command detection failed: $local_build_dir"
# Prompt user to enter a build command
vared -p 'enter comand: ' build
fi
# Redefine the `build` alias and update the `~build` hash directory
alias build="$build"
hash -d build=$local_build_dir
export build_dir=$local_build_dir
export BUILD_DIR=$PWD/$local_build_dir
echo "$build_dir"
# If `--build` is specified then evaluate the command.
if [[ -n $do_build ]]; then
eval build
fi
# Bind C-B to fuzzy find & complete cmake variables.
zle -N .build-var
bindkey '^B' .build-var
}
# Build then run a target residing in `~build/bin`.
build-run() {
local target=$1; shift 1
eval build $target && ~build/bin/$target "$@"
}
# Build then debug a target residing in `~build/bin`.
build-debug() {
local target=$1; shift 1
eval build $target && debug ~build/bin/$target "$@"
}
# Fuzzy find CMake variables, select one to set the variable via a command.
.build-var() {
local var=$(
cat $build_dir/CMakeCache.txt |
grep --color=never -Ex '^\w+:\w+=.*$' |
fzf --layout=reverse --info=hidden --border=rounded \
--cycle --height=50%
)
if [[ -n "$var" ]]; then
if [[ "$BUFFER" = "cmake"* ]]; then
BUFFER="$BUFFER-D$var"
else
BUFFER="cmake -B\$build_dir -D$var"
fi
zle end-of-line
fi
zle reset-prompt
}

View File

@@ -1,105 +0,0 @@
#!/usr/bin/env python3
"""Uninstall a previously installed CMake installation.
Read the ``install_manifest.txt`` file from a CMake build directory
which has been installed then delete the files, and optionally the empty
subdirectories of the install prefix, specified within.
"""
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from os import listdir, path, remove, rmdir
from sys import stderr
def uninstall(build_dir, recursive=False):
"""Uninstall a previoulsy installed CMake installation.
Arguments:
:build_dir: Path to the build directory containing the
``install_manifest.txt`` of files to uninstall.
:recursive: Boolean flag to enable recursively uninstall empty
directories up to the install prefix specified in ``CMakeCache.txt``.
"""
# Get the list of installed files from the install_manifest.txt
install_manifest_path = path.join(build_dir, 'install_manifest.txt')
with open(install_manifest_path, 'r') as install_manifest_file:
install_manifest = sorted(install_manifest_file.read().splitlines())
# Delete files from the filesystem
removed = []
directories = []
for entry in install_manifest:
directories.append(path.dirname(entry))
if path.isfile(entry):
remove(entry)
removed.append(entry)
if recursive:
# Get the install prefix from CMakeCache.txt
cmakecache_path = path.join(build_dir, 'CMakeCache.txt')
with open(cmakecache_path, 'r') as cmakecache_file:
for line in cmakecache_file.read().splitlines():
if line.startswith('CMAKE_INSTALL_PREFIX'):
prefix = path.normpath(line.split('=')[1])
break
while True:
# Remove duplicates from list
directories = list(dict.fromkeys(directories))
# Find directories in list which are not empty
not_empty = []
for index, entry in enumerate(directories):
if listdir(entry):
not_empty.append(index)
# Remove directories which are not empty from list
for index in reversed(not_empty):
del directories[index]
# Delete directories from the filesystem
to_del = []
to_append = []
for index, entry in enumerate(directories):
rmdir(entry)
removed.append(entry)
to_del.append(index)
parent = path.dirname(entry)
# Add parent directory to the list when not the install prefix
if path.normpath(parent) != prefix:
to_append.append(parent)
# Remove deleted directories from list
for index in reversed(to_del):
del directories[index]
directories += to_append
# Exit loop when no more directories in list
if not directories:
break
return removed
def main():
"""Command line entry point."""
cli = ArgumentParser(description=__doc__,
formatter_class=RawDescriptionHelpFormatter)
cli.add_argument('--version', action='version', version='%(prog)s 0.1.0')
cli.add_argument('-q',
'--quiet',
action='store_true',
help="don't print removed entries to stdout")
cli.add_argument('-r',
'--recursive',
action='store_true',
help='recursively remove empty directories')
cli.add_argument('build_dir',
help='path to the installed CMake build directory')
args = cli.parse_args()
try:
removed = uninstall(args.build_dir, recursive=args.recursive)
if not args.quiet:
for entry in removed:
print(f'-- Uninstalling: {entry}')
except FileNotFoundError as error:
print(f'error: file not found: {error.filename}', file=stderr)
exit(1)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
exit(130)

View File

@@ -1,204 +0,0 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if __linux__
#include <sys/wait.h>
#endif
#ifdef DEBUG
#define check(CONDITION) \
if (CONDITION) { \
fprintf(stderr, "error: %s: %d: %s\n", __FILE__, __LINE__, #CONDITION); \
exit(0); \
}
#else
#define check(CONDITION) \
if (CONDITION) { \
exit(0); \
}
#endif
typedef struct process {
pid_t pid;
FILE *out;
} process_t;
process_t process_open(char *command) {
int fds[2];
check(pipe(fds));
int pid = fork();
check(pid == -1);
if (pid == 0) { // child process
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
dup2(STDOUT_FILENO, STDERR_FILENO);
char *argv[] = {"sh", "-c", command, NULL};
exit(execvp(argv[0], argv));
} else { // parent process
close(fds[1]);
process_t process = {pid, fdopen(fds[0], "rb")};
return process;
}
}
int process_close(process_t process) {
fclose(process.out);
int status;
check(process.pid != waitpid(process.pid, &status, 0));
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
return 0;
}
char *trim(char *str) {
char *end;
while (isspace((unsigned char)*str)) {
str++;
}
if (*str == 0) {
return str;
}
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) {
end--;
}
end[1] = '\0';
return str;
}
char *append(char *buffer, int count, ...) {
va_list list;
va_start(list, count);
for (int i = 0; i < count; i++) {
strcat(buffer, va_arg(list, char *));
}
va_end(list);
return buffer;
}
char *inttostr(char *buffer, int value) {
sprintf(buffer, "%d", value);
return buffer;
}
int main() {
// get the current branch name
process_t process = process_open("git symbolic-ref --short HEAD");
char branch_buf[256] = {};
fread(branch_buf, 1, sizeof(branch_buf), process.out);
if (process_close(process)) {
// current HEAD is not a symbolic ref
process = process_open("git rev-parse --abbrev-ref HEAD");
memset(branch_buf, 0, sizeof(branch_buf));
fread(branch_buf, 1, sizeof(branch_buf), process.out);
check(process_close(process));
if (strcmp("HEAD", trim(branch_buf)) == 0) {
// get the commit hash
process = process_open("git rev-parse --short HEAD");
memset(branch_buf, 0, sizeof(branch_buf));
fread(branch_buf, 1, sizeof(branch_buf), process.out);
check(process_close(process));
}
}
char *branch = trim(branch_buf);
char prompt[1024] = {};
append(prompt, 3, " %{%F{66}%}", branch, "%{%f%}");
// get the upstream remote if one exists
char command[1024] = {};
append(command, 3, "git config branch.", branch, ".remote");
process = process_open(command);
char remote_buf[256] = {};
fread(remote_buf, 1, sizeof(remote_buf), process.out);
if (process_close(process) == 0) {
char *remote = trim(remote_buf);
// get the number of commits ahead of the remote
memset(command, 0, sizeof(command));
process = process_open(append(command, 5,
"git rev-list --right-only refs/remotes/",
remote, "/", branch, "...HEAD --count"));
char count[32] = {};
fread(count, 1, sizeof(count), process.out);
if (process_close(process) == 0 && strcmp("0", trim(count))) {
append(prompt, 2, "", trim(count));
}
// get the number of commits behind the remote
memset(command, 0, sizeof(command));
process = process_open(append(command, 5,
"git rev-list --left-only refs/remotes/",
remote, "/", branch, "...HEAD --count"));
memset(count, 0, sizeof(count));
fread(count, 1, sizeof(count), process.out);
if (process_close(process) == 0 && strcmp("0", trim(count))) {
append(prompt, 2, "", trim(count));
}
}
append(prompt, 1, " ");
// get the status and parse it
process = process_open("git status --porcelain");
char status[2048];
int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;
while (NULL != fgets(status, sizeof(status) - 1, process.out)) {
char X = status[0];
char Y = status[1];
if (X == '?' && Y == '?') {
++untracked;
} else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
(X == 'D' && (Y == 'D' || Y == 'U')) ||
(X == 'U' && (Y == 'A' || Y == 'D' || Y == 'D' || Y == 'U'))) {
++unmerged;
} else {
switch (X) {
case ' ':
switch (Y) {
case 'M': ++modified; break;
case 'D': ++deleted; break;
} break;
case 'D': ++indexed;
switch (Y) {
case ' ': break;
case 'M': ++modified; break;
} break;
case 'M': case 'A': case 'R': case 'C': ++indexed;
switch (Y) {
case ' ': break;
case 'M': ++modified; break;
case 'D': ++deleted; break;
} break;
}
}
}
check(process_close(process));
if (indexed || modified || deleted || unmerged || untracked) { // modified
char int_buf[32];
if (indexed) {
append(prompt, 3, "%{%F{2}%}*", inttostr(int_buf, indexed), "%{%f%}");
}
if (modified) {
append(prompt, 3, "%{%F{1}%}+", inttostr(int_buf, modified), "%{%f%}");
}
if (deleted) {
append(prompt, 3, "%{%F{1}%}-", inttostr(int_buf, deleted), "%{%f%}");
}
if (unmerged) {
append(prompt, 3, "%{%B%F{1}%}×", inttostr(int_buf, unmerged), "%{%f%b%}");
}
if (untracked) {
append(prompt, 1, "%{%F{1}%}…%{%f%}");
}
} else { // clean
append(prompt, 1, "%{%B%F{2}%}✓%{%f%b%}");
}
// print the prompt
puts(prompt);
return 0;
}

View File

@@ -1,92 +0,0 @@
#!/usr/bin/env zsh
error() {
echo "error: $*"
exit 1
}
directories=(
~/.cache/zsh
~/.local/bin
~/.local/share/zsh/plugins
~/.local/share/zsh/site-functions
)
for directory in $directories; do
mkdir -p $directory
done
plugins=(
zsh-users/zsh-autosuggestions
zsh-users/zsh-history-substring-search
zsh-users/zsh-syntax-highlighting
zsh-users/zsh-completions
)
for plugin in $plugins; do
plugin_name=${plugin/*\//}
plugin_directory=~/.local/share/zsh/plugins/$plugin_name
if [ -d $plugin_directory ]; then
if ! git -C $plugin_directory diff-index --quiet HEAD --; then
error $plugin_directory contains unstaged changes
fi
pull=`git -C $plugin_directory pull`
if [ "$pull" != "Already up to date." ] && \
[ "$pull" != "Already up-to-date." ]; then
echo changed pulled $plugin_directory
fi
else
git clone https://github.com/$plugin.git $plugin_directory > /dev/null
echo changed cloned $plugin_directory
fi
old_plugin_directory=~/.config/zsh/$plugin_name
if [ -d $old_plugin_directory ]; then
rm -rf $old_plugin_directory
echo changed removed $old_plugin_directory
fi
done
declare -A symlinks
symlinks=(
~/.config/zsh/zlogin ~/.zlogin
~/.config/zsh/zlogout ~/.zlogout
~/.config/zsh/zprofile ~/.zprofile
~/.config/zsh/zshenv ~/.zshenv
~/.config/zsh/zshrc ~/.zshrc
~/.config/zsh/prompt_fresh_setup
~/.local/share/zsh/site-functions/prompt_fresh_setup
~/.config/zsh/cmake-uninstall ~/.local/bin/cmake-uninstall
~/.config/zsh/$ ~/.local/bin/$
~/.config/zsh/url/url ~/.local/bin/url
)
for completion in ~/.config/zsh/**/_*; do
filename=`basename $completion`
symlinks[$completion]=~/.local/share/zsh/site-functions/$filename
done
completions=( ~/.local/share/zsh/plugins/zsh-completions/src/* )
for completion in $completions; do
filename=`basename $completion`
name=${filename:1}
if command -v $name > /dev/null; then
symlinks[$completion]=~/.local/share/zsh/site-functions/$filename
fi
done
for source in ${(k)symlinks}; do
dest=$symlinks[$source]
if [ -L $dest ]; then
target=`readlink $dest`
if [ "$target" != "$source" ]; then
rm $dest
ln -s $source $dest
echo changed replace incorrect symlink $dest
fi
elif [ -f $dest ]; then
error symlink failed $dest exists but is a regular file
else
ln -s $source $dest
echo changed created symlink $dest
fi
done

View File

@@ -1,14 +0,0 @@
#compdef layout
__get_layouts() {
ls -1 ~/.local/share/tmux/layouts 2>/dev/null | \
while read -r layout; do echo $layout; done
}
__layouts() {
local -a layouts
layouts=(${(fo)"$(__get_layouts)"})
_describe 'layout' layouts
}
_arguments ':layout:__layouts'

View File

@@ -1,10 +0,0 @@
layout() {
if [[ "$1" == "" ]]; then
echo "usage: layout <layout> [name]"
else
~/.local/share/tmux/layouts/$1
if [[ "$2" != "" ]]; then
tmux rename-window $2
fi
fi
}

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env zsh
# Loop over available completions and add existing commands to array.
local -a completions
completions=(~/.config/zsh/zsh-completions/src/*)
local -a command_list
for completion in $completions; do
local filename=$(basename $completion)
local name=${filename:1}
if command -v $name &> /dev/null; then
command_list+=($name)
fi
done
# Print JSON array of commands Ansible can consume.
echo '['
local length=${#command_list[@]}
for (( i = 1; i < $length; i++ )); do
echo " \"${command_list[$i]}\","
done
echo " \"${command_list[-1]}\""
echo ']'

View File

@@ -1,6 +0,0 @@
#!/bin/bash
for d in /sys/kernel/iommu_groups/*/devices/*; do
n=${d#*/iommu_groups/*}; n=${n%%/*}
printf '%s ' "$n"
lspci -nns "${d##*/}"
done

View File

@@ -1,13 +0,0 @@
#compdef note
__get_notes() {
ls -1 ~/Sync/Notes 2>/dev/null | while read -r note; do echo $note; done
}
__notes() {
local -a notes
notes=(${(fo)"$(__get_notes)"})
_describe 'notes' notes
}
_arguments ':notes:__notes'

View File

@@ -1,8 +0,0 @@
# TODO: Support opening multiple notes in buffers or tabs
note() {
if [[ "$1" == "" ]]; then
echo "usage: note \"<title>\""
else
vim -c "Note $1"
fi
}

View File

@@ -1,18 +1,17 @@
prompt_fresh_help() { prompt_fresh_help() {
echo "\ echo "\
options: options:
-a enable almostontop like behaviour -a enable almostontop like behaviour"
-r recompile git-prompt executable"
} }
prompt_fresh_setup() { prompt_fresh_setup() {
fresh_compile_git_prompt
# Parse options # Parse options
local almostontop=0 local almostontop=0
local recompile=0 while getopts 'a' opt; do
while getopts 'ar' opt; do
case $opt in case $opt in
a) local almostontop=1 ;; a) local almostontop=1 ;;
r) local recompile=1 ;;
*) prompt -h fresh; return 1 ;; *) prompt -h fresh; return 1 ;;
esac esac
done done
@@ -20,173 +19,199 @@ prompt_fresh_setup() {
autoload -U add-zsh-hook autoload -U add-zsh-hook
# Hook to print the first line of the "two line" prompt, this line is not # Hook to print the first line of the "two line" prompt, this line is not
# actually part of the prompt so does not get redrawn, this is preferable # actually part of the prompt so does not get redrawn which can sometimes
# since sometimes lines before the prompt can disappear. # cause lines before the prompt to disappear.
add-zsh-hook precmd fresh_line_one add-zsh-hook precmd fresh_line_one
if [ $almostontop -eq 1 ]; then if [ $almostontop -eq 1 ]; then
# Hook to clear the screen then prints the prompt with previous command at # Hook to clear the screen then prints the prompt with previous command at
# the top of the screen. # the top of the screen.
add-zsh-hook precmd fresh_almostontop_precmd add-zsh-hook preexec fresh_almostontop
add-zsh-hook preexec fresh_almostontop_preexec
else else
add-zsh-hook -d preexec fresh_almostontop_preexec add-zsh-hook -d preexec fresh_almostontop
add-zsh-hook -d precmd fresh_almostontop_precmd
fi fi
[ $recompile -eq 1 ] && prompt_cleanup if [ "$USERNAME" = "root" ]; then
fresh_compile_git_prompt local user="%{%F{9}%}%n%{%f%}"
if [ "$USER" = "root" ]; then
local user="%{%F{1}%}%n%{%f%}"
else else
local user="%{%F{35}%}%n%{%f%}" local user="%{%F{35}%}%n%{%f%}"
fi fi
local userhost=$USER if [ "$SSH_CONNECTION" != "" ]; then
if [ "$SSH_CONNECTION" != "" ] || [ "$container" != "" ]; then local user="%{%F{35}%}$user%{%f%}@%{%F{244}%}%M%{%f%}"
local user="$user@%{%F{244}%}%M%{%f%}"
local userhost="$userhost@`hostname`"
fi fi
PS1="$user " local length=`fresh_visible_length "$user"`
PS2="${(l:${#userhost}:: :)} "
prompt_opts=(percent sp subst) PS1="«$user» "
PS2="${(l:$length + 1:: :)}» "
RPS1='$(fresh_rprompt)'
prompt_opts=(cr percent sp subst)
} }
prompt_cleanup() { prompt_cleanup() {
[ -f ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt ] && \ if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi
rm ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt
} }
fresh_line_one() { fresh_line_one() {
# First get the last commands exit code before doing anything # First get the last commands exit code before doing anything
local exit_code=$? local exit_code=$?
# Clean up if fresh is no longer the current prompt theme
if [[ "`prompt -c | tail -1 | xargs`" != "fresh"* ]]; then
add-zsh-hook -d precmd fresh_line_one
return
fi
# Construct the time and directory portions of the prompt # Construct the time and directory portions of the prompt
local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}" local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}"
[[ -n $SANDBOX_HOME ]] && \ local directory="%{%F{37}%}%~%{%f%}"
local directory="%{%F{220}%}$SANDBOX_NAME${PWD#$SANDBOX_HOME}%{%f%}" || \
local directory="%{%F{37}%}%~%{%f%}"
# Check we are in a git repository # Check we are in a git repository
local git=`${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt` if `git rev-parse --git-dir > /dev/null 2>&1`; then
# Get git branch name & exit early if not found
local branch="`git symbolic-ref HEAD 2> /dev/null`"
if [ "" = "$branch" ]; then
local branch="`git rev-parse --abbrev-ref HEAD`"
if [ "HEAD" = "$branch" ]; then
local branch="`git rev-parse --short $branch`"
fi
fi
# If the last command failed, display its error code at the right if [ "$branch" != "" ]; then
if [[ $exit_code -ne 0 ]]; then local branch=${branch#refs/heads/}
case $exit_code in # Get the number of commits ahead/behind from the remote branch
129) exit_code="SIGHUP" ;; # 128 + 1 local remote="`git config branch.$branch.remote 2> /dev/null`"
130) exit_code="SIGINT" ;; # 128 + 2 if [ "" != "remote" ]; then
131) exit_code="SIGQUIT" ;; # 128 + 3 local branches="refs/remotes/$remote/$branch...HEAD"
132) exit_code="SIGILL" ;; # 128 + 4 local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
133) exit_code="SIGTRAP" ;; # 128 + 5 if [ "0" -ne "$nahead" ]; then local ahead="↑$nahead"; fi
134) exit_code="SIGABRT" ;; # 128 + 6 local nbehind=`git rev-list --left-only $branches --count 2> /dev/null`
134) exit_code="SIGIOT" ;; # 128 + 6 if [ "0" -ne "$nbehind" ]; then local behind="↓$nbehind"; fi
135) exit_code="SIGBUS" ;; # 128 + 7 fi
136) exit_code="SIGFPE" ;; # 128 + 8
137) exit_code="SIGKILL" ;; # 128 + 9 # Construct the git prompt
138) exit_code="SIGUSR1" ;; # 128 + 10 local git="%{%F{66}%}$branch%{%f%}$ahead$behind "
139) exit_code="SIGSEGV" ;; # 128 + 11
140) exit_code="SIGUSR2" ;; # 128 + 12 if [ -f ~/.cache/zsh/git-prompt ]; then
141) exit_code="SIGPIPE" ;; # 128 + 13 # Get the change counts from git-prompt
142) exit_code="SIGALRM" ;; # 128 + 14 local counts=(`~/.cache/zsh/git-prompt`)
143) exit_code="SIGTERM" ;; # 128 + 15
144) exit_code="SIGSTKFLT" ;; # 128 + 16 # Parse the results from git-prompt
145) exit_code="SIGCHLD" ;; # 128 + 17 if [ 0 -eq ${#counts[@]} ]; then # clean
146) exit_code="SIGCONT" ;; # 128 + 18 local git="$git%{%B%F{2}%}✓%{%f%b%}";
147) exit_code="SIGSTOP" ;; # 128 + 19 else
148) exit_code="SIGTSTP" ;; # 128 + 20 if [ 0 != $counts[1] ]; then # indexed
149) exit_code="SIGTTIN" ;; # 128 + 21 local git="$git%{%F{2}%}*$counts[1]%{%f%}"
150) exit_code="SIGTTOU" ;; # 128 + 22 fi
151) exit_code="SIGURG" ;; # 128 + 23 if [ 0 != $counts[2] ]; then # modified
152) exit_code="SIGXCPU" ;; # 128 + 24 local git="$git%{%F{1}%}+$counts[2]%{%f%}";
153) exit_code="SIGXFSZ" ;; # 128 + 25 fi
154) exit_code="SIGVTALRM" ;; # 128 + 26 if [ 0 != $counts[3] ]; then # deleted
155) exit_code="SIGPROF" ;; # 128 + 27 local git="$git%{%F{1}%}-$counts[3]%{%f%}";
156) exit_code="SIGWINCH" ;; # 128 + 28 fi
157) exit_code="SIGIO" ;; # 128 + 29 if [ 0 != $counts[4] ]; then # unmerged
158) exit_code="SIGPWR" ;; # 128 + 30 local git="$git%{%B%F{1}%}×$counts[4]%{%f%b%}";
159) exit_code="SIGSYS" ;; # 128 + 31 fi
esac if [ 0 != $counts[5] ]; then # untracked
local result=" %{%B%F{1}%}$exit_code%{%f%b%}" local git="$git%{%F{1}%}…%{%f%}";
fi
fi
fi
fi
fi fi
# Unset vim/tmux navigate flag to handle C-z and multiple vim jobs. # Construct the prompt string
[[ ! -z "$TMUX" ]] && \ local line="$time_stamp $directory $git"
[[ "`tmux show-window-options`" = *"@vim$TMUX_PANE"* ]] && \
tmux set-window-option -u @vim$TMUX_PANE
# If a virtualenv is enabled, display it's basename
if [[ ! -z "$VIRTUAL_ENV" ]]; then
local py=" %{%F{4}%}py%{%f%}%{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
fi
# If docker-machine env is active, display the machines name
if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
local docker=" %{%F{6}%}$DOCKER_MACHINE_NAME%{%f%}"
fi
# Print the first line of the prompt # Print the first line of the prompt
print -P "$time_stamp $directory$git$py$docker$result" if [[ $exit_code -eq 0 ]]; then
print -P "$line"
else
# The last command failed, display its error code at the right
local result="%{%B%F{1}%}$exit_code%{%f%b%}"
local length=`fresh_visible_length "$line$result"`
print -P "$line${(l:COLUMNS - $length - 1:: :)}$result"
fi
} }
# Executed before each prompt. fresh_rprompt() {
fresh_almostontop_precmd() { rprompt=""
# CSI ESC[6n gets the cursor position in the form ESC[<row>;<column>R if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
printf "\033[6n" rprompt="$rprompt %{%F{3}%}$DOCKER_MACHINE_NAME%{%f%}"
# Discard prefix delimited by [ fi
read -s -d [ if [[ ! -z "$VIRTUAL_ENV" ]]; then
# Store the <row> delimited by ; in row_before rprompt="$rprompt %{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
read -s -d \; row_before fi
# Discard suffix delimted by R otherwise it is output to the tty echo $rprompt
read -s -d R
} }
# Executed just after a command has been read and is about to be executed. fresh_almostontop() {
fresh_almostontop_preexec() { clear
# CSI ESC[6n gets the cursor position in the form ESC[<row>;<column>R fresh_line_one
printf "\033[6n" print -P "$PROMPT"'$1'
# Discard prefix delimited by [ }
read -s -d [
# Store the <row> delimited by ; in row fresh_visible_length() {
read -s -d \; row echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} ))
# Discard suffix delimted by R otherwise it is output to the tty
read -s -d R
# Move the cursor to the bottom of the terminal
# CSI ESC[<num>B moves the cursor down <num> lines
let "down = $LINES - $row"
printf "\033[${down}B"
# Calculate the number of lines in the prompt
let "prompt_lines = ($row - $row_before) + 2"
# Print new lines to push the old command out of view
let "new = $row - $prompt_lines"
for (( i = 0; i < $new; i++ )); do
printf "\n"
done
# Move the cursor to the line below the prompt
# CSI ESC[<num>A moves the cursor up <num> lines
let "up = $LINES - $prompt_lines"
printf "\033[${up}A"
} }
fresh_compile_git_prompt() { fresh_compile_git_prompt() {
# Compile a simple C program which parses the output of `git status # Compile a simple C executable which parses the output of `git status
# --procelain` to greatly decrease the time taken to draw the prompt # --procelain` to greatly decrease the time taken to render the prompt
[ ! -d ${XDG_CACHE_HOME:-$HOME/.cache}/zsh ] && \ local cache=~/.cache/zsh
mkdir -p ${XDG_CACHE_HOME:-$HOME/.cache}/zsh if [ ! -d $cache ]; then mkdir -p $cache; fi
if [ ! -f ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt ]; then if [ ! -f $cache/git-prompt ]; then
cc -std=gnu99 -O3 -DNDEBUG -Wno-unused-result \ cc -x c -std=gnu99 -O3 -DNDEBUG -o $cache/git-prompt - 2> /dev/null << EOF
${XDG_CONFIG_HOME:-$HOME/.config}/zsh/git-prompt.c \ #include <stdio.h>
-o ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt #include <stdlib.h>
int main() {
FILE *file = popen("git status --porcelain", "r");
if (NULL == file) { return 0; }
char status[2048];
int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;
while (NULL != fgets(status, sizeof(status) - 1, file)) {
char X = status[0];
char Y = status[1];
if (X == '?' && Y == '?') {
++untracked;
} else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
(X == 'D' && (Y == 'D' || Y == 'U')) ||
(X == 'U' && (Y == 'A' || Y == 'D' || Y == 'D' || Y == 'U'))) {
++unmerged;
} else {
switch (X) {
case ' ':
switch (Y) {
case 'M': ++modified; break;
case 'D': ++deleted; break;
} break;
case 'D': ++indexed;
switch (Y) {
case ' ': break;
case 'M': ++modified; break;
} break;
case 'M': case 'A': case 'R': case 'C': ++indexed;
switch (Y) {
case ' ': break;
case 'M': ++modified; break;
case 'D': ++deleted; break;
} break;
}
}
}
pclose(file);
if (indexed || modified || deleted || unmerged || untracked) {
printf("%d %d %d %d %d", indexed, modified, deleted, unmerged, untracked);
}
return 0;
}
EOF
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "git-prompt was not compiled, is a C99 toolchain installed?" echo "git prompt disabled, are the system development headers installed?"
fi fi
fi fi
} }

View File

@@ -1,45 +0,0 @@
#compdef sandbox
__sandboxes() {
local -a sandboxes
sandboxes=(${(fo)"$(ls $SANDBOX_ROOT 2> /dev/null)"})
_describe 'in' sandboxes
}
_sandbox() {
local context curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'1: :->cmd' \
'*:: :->args'
case $state in
(cmd)
local commands; commands=(
'create:Create a new sandbox'
'rename:Rename an existing sandbox'
'destroy:Destroy an existing sandbox'
'list:Show all existing sandboxes'
'enable:Enable an existing sandbox'
'disable:Disable the current sandbox'
)
_describe -t commands 'sandbox command' commands "$@"
;;
(args)
curcontext="${curcontext%:*:*}:sandbox-cmd-$words[1]:"
case $line[1] in
(create)
_arguments -C '--git[repository to clone]: :'
;;
(rename|enable|destroy)
_arguments -C '1:: :__sandboxes'
;;
(list|disable)
;;
esac
esac
}
_sandbox "$@"

View File

@@ -1,127 +0,0 @@
if [[ "" == $SANDBOX_ROOT ]]; then
export SANDBOX_ROOT=$HOME/Sandbox
fi
sandbox() {
local usage="\
usage: sandbox [-h] {create,rename,destroy,enable,disable,list} ..
sandbox create [--git <repo>] <name>
sandbox rename <old-name> <new-name>
sandbox destroy <name>
sandbox enable <name>
sandbox disable
sandbox list"
error() { print -P "%F{red}error:%f $1" }
local cmd=$1
[[ -z "$cmd" ]] && \
error "missing command\n$usage" && return 1
shift 1
case $cmd in
create)
# Parse command arguments.
local git=false
for arg in $@; do
if [ "${arg[1]}" = - ]; then
if [ "$git" = true ]; then
error "invalid --git <repo> $arg\n$usage" && return 1
elif [ "$arg" = --git ]; then
git=true
else
error "invalid option $arg\n$usage" && return 1
fi
else
if [ "$git" = true ]; then
local repo=$arg
git=false
elif [[ -z "$name" ]]; then
error "invalid argument $arg\n$usage" && return 1
else
local name=$arg
fi
fi
done
unset git
[[ -z "$name" ]] && \
error "missing argument <name>\n$usage" && return 1
local sandbox=$SANDBOX_ROOT/$name
[[ -d "$sandbox" ]] && \
error "sandbox already exists $name" && return 1
if [[ -n "$repo" ]]; then
mkdir -p $SANDBOX_ROOT &> /dev/null
git clone $repo $sandbox
cd $sandbox
else
mkdir -p $sandbox &> /dev/null
cd $sandbox
git init &> /dev/null
fi
echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> $sandbox/.enter
echo "SANDBOX_NAME=$name" >> $sandbox/.enter
_autoenv_authorized $sandbox/.enter yes
echo "unset SANDBOX_NAME" >> $sandbox/.exit
echo "unset SANDBOX_HOME" >> $sandbox/.exit
_autoenv_authorized $sandbox/.exit yes
_autoenv_enter $sandbox
;;
rename)
local old_name=$1 new_name=$2
[[ -z "$old_name" ]] && \
error "missing argument <old-name>\n$usage" && return 1
[[ -z "$new_name" ]] && \
error "missing argument <new-name>\n$usage" && return 1
local old=$SANDBOX_ROOT/$old_name new=$SANDBOX_ROOT/$new_name
[[ ! -d "$old" ]] && \
error "sandbox does not exist $old_name" && return 1
[[ -d "$new" ]] && \
error "sandbox already exists $new_name" && return 1
[[ "$PWD" = "$old"* ]] && _autoenv_exit $PWD
mv $old $new
sed -i "s/$old_name/$new_name/g" $new/.enter
_autoenv_authorized $new/.enter yes
_autoenv_authorized $new/.exit yes
[[ "$PWD" = "$old"* ]] && cd $new
;;
destroy)
local name=$1
[[ -z "$name" ]] && \
error "missing argument <name>\n$usage" && return 1
local sandbox=$SANDBOX_ROOT/$name
[[ ! -d $sandbox ]] && \
error "sandbox does not exist $name" && return 1
[[ "$PWD" = "$sandbox"* ]] && cd ~
rm -rf $sandbox
;;
list)
ls -1 $SANDBOX_ROOT | less -F -K -R -X
;;
enable)
local name=$1
[[ -z "$name" ]] && \
error "missing argument <name>\n$usage" && return 1
local sandbox=$SANDBOX_ROOT/$name
[[ ! -d $sandbox ]] && \
error "sandbox does not exist $name" && return 1
export SANDBOX_RETURN=$PWD
cd $sandbox
;;
disable)
[[ -z "$SANDBOX_RETURN" ]] && \
error "sandbox is not currently active" && return 1
cd $SANDBOX_RETURN
unset SANDBOX_RETURN
;;
*)
error "invalid sandbox command: $cmd" && return 1
;;
esac
}

View File

@@ -1,32 +0,0 @@
#compdef session
__session_sessions() {
list() {
for item in $HOME/.local/share/tmux/layouts/session-*; do
item=${item#$HOME/.local/share/tmux/layouts/}
echo ${item#session-}
done
}
local -a sessions
sessions=(${(fo)"$(list)"})
_describe 'session' sessions
}
__session_hosts() {
list() {
declare -A hosts
if [ -f ~/.config/session ]; then
source ~/.config/session
for key val in "${(@kv)hosts}"; do
echo $key
done
fi
}
local -a hosts
hosts=(${(fo)"$(list)"})
_describe 'host' hosts
}
_arguments \
':session:__session_sessions' \
':host:__session_hosts'

View File

@@ -1,39 +0,0 @@
session() {
if [[ "$1" == "" ]]; then
echo "usage: session [-h] <name> [<host>]"
elif [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
echo "usage: session [-h] <name> [<host>]
Create or attach to a tmux session by name either locally on on a remote host
via ssh.
"
else
local name=$1
local host=$2
if [[ "$3" != "" ]]; then
echo "$fg[red]error:$reset_color invalid argument: $3"
return 1
fi
declare -A hosts
if [ -f ~/.config/session ]; then
source ~/.config/session
fi
local url=$hosts[$host]
host=${url:-$host}
if [[ "$TMUX" == "" ]]; then
local cmd="tmux new-session -As $name"
if [[ "$host" != "" ]]; then
cmd="ssh $host -t $cmd"
fi
eval $cmd
else
if [[ "$host" != "" ]]; then
echo "$fg[red]error:$reset_color <host> not allowed inside tmux session"
return 1
fi
tmux list-sessions | grep "$name:" &> /dev/null || \
tmux new-session -Ads $name -c $HOME
tmux switch-client -t $name
fi
fi
}

View File

@@ -1,22 +0,0 @@
#compdef url
_url() {
local ret=1 context curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C -w -s \
'(-h --help)'{-h,--help}'[show this help message and exit]' \
'1: :->command'
case $state in
(command)
declare -a commands
local commands=(
encode:'encode unencoded text'
decode:'decode encoded text'
)
_describe -t commands command commands && ret=0 ;;
esac
return ret
}

27
url/url
View File

@@ -1,27 +0,0 @@
#!/usr/bin/env python
"""URL encode or decode text."""
from argparse import ArgumentParser
from urllib import parse
def main():
cli = ArgumentParser(description=__doc__)
cli.add_argument('command',
choices=['encode', 'decode'],
help='type of processing to perform on text')
cli.add_argument('text',
nargs='?',
help='optional text read from stdin when omitted')
args = cli.parse_args()
print({
'encode': parse.quote_plus,
'decode': parse.unquote_plus,
}[args.command](args.text if args.text else input()))
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
exit(130)

View File

@@ -1,195 +0,0 @@
# A collection of various shell utilities.
autoload colors && colors
# Abstract different ways to copy to the clipboard.
if [ -n "$SSH_CONNECTION" ] ; then
# Use OSC-52 to set the clipboard
alias copy='base64 | xargs -0 printf "\033]52;c;%s\a"'
elif [ "`uname`" = "Darwin" ]; then
# Use pbcopy to set the clipboard
alias copy='pbcopy'
elif which xclip &> /dev/null; then
# Use xclip to set the clipboard
alias copy='xclip -selection c'
fi
# Abstract different ways to paste from the clipboard.
# TODO: Use OSC-52 to get the clipboard, not widely supported though
if [ "`uname`" = "Darwin" ]; then
# Use pbpaste to get the clipboard
alias paste='pbpaste'
elif which xclip &> /dev/null; then
# Use xclip to get the clipboard
alias paste='xclip -selection c -o'
fi
# Passthrough an escape sequences tmux doesn't know about.
tmux-dcs-passthrough() {
local escape_sequence=$1
if [ -n "$TMUX" ]; then
# Replace single \x1b or \033 with two, this is required for tmux to
# properly pass-through the escape sequence.
escape_sequence=${escape_sequence//\\x1b/\\x1b\\x1b}
escape_sequence=${escape_sequence//\\033/\\x1b\\x1b}
printf '\x1bPtmux;\x1b'"$escape_sequence"'\x1b\\'
else
printf "$escape_sequence"
fi
}
# OSC 9 - Post a notification - supported by iTerm2, kitty, WezTerm, others?
notify() {
tmux-dcs-passthrough '\x1b]9;'"$*"'\x7'
}
# Send a desktop notification when long running commands complete.
notify_command_threshold=60
notify_ignore_list=(
bash
bat
btop
cat
cmatrix
fg
gh
git
glab
htop
ipython
man
nvim
ping
podman
python
session
slides
ssh
sudo
sudoedit
tmux
top
vi
vim
watch
zsh
)
notify-ignore() {
for ignore in $notify_ignore_list; do
if [[ "$1" = "$ignore"* ]]; then
return 0
fi
done
return 1
}
notify-preexec() {
if notify-ignore $1; then
return
fi
notify_command_start=`date +%s`
notify_command=$1
}
add-zsh-hook preexec notify-preexec
notify-precmd() {
if ! [[ -z $notify_command_start ]]; then
local notify_command_end=`date +%s`
local notify_command_time=$(($notify_command_end - $notify_command_start))
if [[ $notify_command_time -gt $notify_command_threshold ]]; then
notify "completed: $notify_command"
fi
unset notify_command
unset notify_command_start
fi
}
add-zsh-hook precmd notify-precmd
# Detect the type and extract an archive file.
extract() {
if [ -f $1 ]; then
case $1 in
*.tar.bz2) tar xvjf $1 ;;
*.tar.gz) tar xvzf $1 ;;
*.tar.xz) [ `"uname"` = "Darwin" ] && tar xvJf $1 || tar xf $1 ;;
*.bz2) bunzip2 $1 ;;
*.rar) unrar x $1 ;;
*.gz) gunzip $1 ;;
*.tar) tar xvf $1 ;;
*.tbz2) tar xvjf $1 ;;
*.tgz) tar xvzf $1 ;;
*.zip) unzip $1 ;;
*.Z) uncompress $1 ;;
*.7z) 7zr x $1 ;;
*) echo "$fg[red]error:$reset_color unable to extract '$1'" ;;
esac
else
echo "$fg[red]error:$reset_color file not found '$1'"
fi
}
if which bat &> /dev/null; then
# Wrap bat to specify a theme, always enable color, pipe the output to less.
# Both --theme and --color can be specified multiple times and will override
# these defaults.
bat() {
command bat --theme='TwoDark' --color always --paging auto "$@"
}
elif which batcat &> /dev/null; then
bat() {
command batcat --theme='TwoDark' --color always --paging auto "$@"
}
fi
if which docker-machine &> /dev/null; then
# Wrap the docker command to print a message if a docker-machine is not
# running, rather than just stating it can not find it's socket.
docker() {
command docker "$@"
if ! docker-machine active &> /dev/null; then
echo "$fg[red]error:$reset_color no active host found, run:" \
"docker-machine start <machine>"
return 1
fi
}
# Wrap the docker-machine command to automatically update the environment.
# When a machine is started, set the environment variables provided by
# docker-machine env <machine>. When a machine is stopped, unset the same
# variables.
docker-machine() {
command docker-machine "$@"
if [ "start" = "$1" ]; then
eval `docker-machine env $2`
elif [ "stop" = "$1" ]; then
unset DOCKER_MACHINE_NAME
unset DOCKER_CERT_PATH
unset DOCKER_HOST
unset DOCKER_TLS_VERIFY
fi
}
fi
ls-iommu() {
$HOME/.config/zsh/ls-iommu.sh | sort -n
}
# Fuzzy history search with fzf
function .fzf-history-search() {
local selected
selected=$(
cat $HISTFILE | # get entire history
sed 's/ *[0-9]* *//' | # remove cruft
awk '!seen[$0]++' | # remove duplicates
fzf --layout=reverse --tac --cycle --info=hidden \
--border=rounded --height=50%
)
if [[ -n "$selected" ]]; then
BUFFER="$selected"
zle end-of-line
fi
zle reset-prompt
}
zle -N .fzf-history-search
bindkey '^R' .fzf-history-search

View File

@@ -1,7 +1,2 @@
# .zprofile [1] Used for executing user's commands at start, will be sourced # .zprofile [1] Used for executing user's commands at start, will be sourced
# when starting as a login shell. # when starting as a login shell.
# macOS is obnoxious and overwrites the PATH from a users ~/.zshenv, which is
# sourced first, in /etc/zprofile by calling `/usr/libexec/path_helper -s` so
# this is required so that PATH is once again set to the desired value.
[ `uname` = Darwin ] && source ~/.zshenv

123
zshenv
View File

@@ -2,52 +2,18 @@
# contain commands that produce output or assume the shell is attached to a # contain commands that produce output or assume the shell is attached to a
# tty. This file will always be sourced. # tty. This file will always be sourced.
[ -f ~/.config/zsh/zshenv.local ] && source ~/.config/zsh/zshenv.local
# Ensure cache and state directories exist
[ ! -d -${XDG_CACHE_HOME:-$HOME/.cache}/zsh ] && \
mkdir -p ${XDG_CACHE_HOME:-$HOME/.cache}/zsh
[ ! -d -${XDG_STATE_HOME:-$HOME/.local/state}/zsh ] && \
mkdir -p ${XDG_STATE_HOME:-$HOME/.local/state}/zsh
# Enable saving command history to file # Enable saving command history to file
HISTFILE=${XDG_STATE_HOME:-$HOME/.local/state}/zsh/histfile [ ! -d $HOME/.cache/zsh ] && mkdir -p $HOME/.cache/zsh
HISTSIZE=20000 HISTFILE=$HOME/.cache/zsh/histfile
SAVEHIST=20000 HISTSIZE=5000
SAVEHIST=5000
# Migrate histfile from cache to state directory
! [ -f $HISTFILE ] && [ -f $HOME/.cache/zsh/histfile ] && \
mv $HOME/.cache/zsh/histfile \
${XDG_STATE_HOME:-$HOME/.local/state}/zsh/histfile
# Remove vi mode switch delay
KEYTIMEOUT=1
# Enable time stats for long lasting commands # Enable time stats for long lasting commands
REPORTTIME=5 REPORTTIME=5
# Add ccache compiler aliases to PATH and use XDG base dir paths # Add Homebrew python to PATH on macOS if present
if [ `uname` = Darwin ]; then [ "`uname`" = "Darwin" ] && [ -d /usr/local/opt/python/libexec/bin ] &&
if [ `uname -m` = arm64 ]; then PATH=/usr/local/opt/python/libexec/bin:$PATH
homebrew_root=/opt/homebrew
[ -d /opt/homebrew/bin ] && \
PATH=$homebrew_root/bin:$PATH
else
homebrew_root=/usr/local
fi
[ -d $homebrew_root/opt/python/libexec/bin ] && \
PATH=$homebrew_root/opt/python/libexec/bin:$PATH
[ -f $homebrew_root/bin/ccache ] && \
PATH=$homebrew_root/opt/ccache/libexec:$PATH
elif [ -f /usr/bin/ccache ]; then
if [ -d /usr/lib/ccache/bin ]; then
PATH=/usr/lib/ccache/bin:$PATH
elif [ -d /usr/lib/ccache ]; then
PATH=/usr/lib/ccache:$PATH
fi
fi
export CCACHE_CONFIGPATH=${XDG_CONFIG_HOME:-$HOME/.config}/ccache
export CCACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/ccache
# Add ~/.local to the environment # Add ~/.local to the environment
fpath+=$HOME/.local/share/zsh/site-functions fpath+=$HOME/.local/share/zsh/site-functions
@@ -55,36 +21,6 @@ PATH=$HOME/.local/bin:$PATH
MANPATH=$HOME/.local/share/man:$MANPATH MANPATH=$HOME/.local/share/man:$MANPATH
INFOPATH=$HOME/.local/share/info:$INFOPATH INFOPATH=$HOME/.local/share/info:$INFOPATH
# Add default CMake options
command -v ninja &> /dev/null && \
export CMAKE_GENERATOR=Ninja
export CMAKE_EXPORT_COMPILE_COMMANDS=ON
export CMAKE_COLOR_DIAGNOSTICS=ON
# Remove duplicates from environment variables
typeset -U fpath
typeset -U PATH; export PATH
typeset -U MANPATH; export MANPATH
typeset -U INFOPATH; export INFOPATH
# Set default editor.
if command -v nvim &> /dev/null; then
export EDITOR=`command -v nvim`
# Also use nvim for man pages
export MANPAGER='nvim +Man!'
elif command -v vim &> /dev/null; then
export EDITOR=`command -v vim`
fi
export GIT_EDITOR=$EDITOR
if command -v fzf &> /dev/null; then
export FZF_DEFAULT_OPTS='--no-bold
--color=fg:#c5c9c5,fg+:#c5c9c5,bg:#000000,bg+:#393836
--color=hl:#8ea4a2,hl+:#8ea4a2,info:#afaf87,marker:#C8C093
--color=prompt:#C8C093,spinner:#8992a7,pointer:#FF9E3B,header:#87afaf
--color=gutter:#000000,border:#54546D,label:#aeaeae,query:#c5c9c5'
fi
# Use ~/.local for pip installs on macOS # Use ~/.local for pip installs on macOS
[ "`uname`" = "Darwin" ] && export PYTHONUSERBASE=$HOME/.local [ "`uname`" = "Darwin" ] && export PYTHONUSERBASE=$HOME/.local
@@ -96,49 +32,6 @@ export LESS_TERMCAP_so=`printf "\e[1;40;32m"`
export LESS_TERMCAP_se=`printf "\e[0m"` export LESS_TERMCAP_se=`printf "\e[0m"`
export LESS_TERMCAP_us=`printf "\e[0;34m"` export LESS_TERMCAP_us=`printf "\e[0;34m"`
export LESS_TERMCAP_ue=`printf "\e[0m"` export LESS_TERMCAP_ue=`printf "\e[0m"`
# Disable storing less history
export LESSHISTFILE=/dev/null
# Force GoogleTest to output colors
export GTEST_COLOR=yes
# Allow completions for GoogleTest break on failure
export GTEST_BREAK_ON_FAILURE=0
# Force CTest to verbose output
export CTEST_OUTPUT_ON_FAILURE=1
# User ~/.local/share for persistent pylint data
export PYLINTHOME=~/.local/share/pylint
# Disable virtualenv prompt # Disable virtualenv prompt
export VIRTUAL_ENV_DISABLE_PROMPT=1 VIRTUAL_ENV_DISABLE_PROMPT=1
# If pinentry-curses exists, use it for lastpass-cli
command -v pinentry-curses &> /dev/null && \
export LPASS_PINENTRY=pinentry-curses
# Teach these some XDG Base Directory Spec manners
export IPYTHONDIR=${XDG_CONFIG_HOME:-$HOME/.config}/ipython
command -v cargo &> /dev/null && \
export CARGO_HOME=$HOME/.local/share/cargo
if command -v ccache &> /dev/null; then
export CCACHE_CONFIGPATH=${XDG_CONFIG_HOME:-$HOME/.config}/ccache.conf
export CCACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/ccache
fi
command -v conan &> /dev/null && \
export CONAN_USER_HOME=$HOME/.local/share/conan
command -v docker &> /dev/null && \
export DOCKER_CONFIG=$HOME/.local/share/docker
export GTK_RC_FILES=${XDG_CONFIG_HOME:-$HOME/.config}/gtk/gtkrc
export GTK2_RC_FILES=${XDG_CONFIG_HOME:-$HOME/.config}/gtk-2.0/gtkrc
export PYLINTHOME=${XDG_CACHE_HOME:-$HOME/.cache}/pylint
command -v rustup &> /dev/null && \
export RUSTUP_HOME=$HOME/.local/share/rustup
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/wget/rc ] && \
export WGETRC=${XDG_CONFIG_HOME:-$HOME/.config}/wget/rc
# TODO: terminfo
export GOBIN=$HOME/.local/bin
export GOPATH=$HOME/.local/share/go
export GOCACHE=${XDG_CACHE_HOME:-$HOME/.cache}/go/build
export GOMODCACHE=${XDG_CACHE_HOME:-$HOME/.cache}/go/pkg/mod
export GOTMPDIR=${XDG_CACHE_HOME:-$HOME/.cache}/go/tmp
export PIXI_HOME=$HOME/.local
export PIXI_NO_PATH_UPDATE=1

183
zshrc
View File

@@ -3,61 +3,25 @@
# Load plugin scripts # Load plugin scripts
source-plugin() { source-plugin() {
local shared_plugin=${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins/$1/$1.plugin.zsh [ -d ~/.config/zsh/$1 ] && \
local local_plugin=${XDG_CONFIG_HOME:-$HOME/.config}/zsh/$1/$1.plugin.zsh source ~/.config/zsh/$1/$1.plugin.zsh || \
if [ -f $shared_plugin ]; then
source $shared_plugin
elif [ -f $local_plugin ]; then
source $local_plugin
else
echo "zsh plugin not found: $1" echo "zsh plugin not found: $1"
fi
} }
# Fish like automatic suggestions from command history
source-plugin zsh-autosuggestions source-plugin zsh-autosuggestions
source-plugin zsh-history-substring-search
source-plugin zsh-syntax-highlighting
source-plugin autoenv
# Disable non end-of-line autosuggest accept widgets # Disable non end-of-line autosuggest accept widgets
ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(end-of-line vi-end-of-line) ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(end-of-line vi-end-of-line)
# Search history with a command substring
source-plugin zsh-history-substring-search
HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND=
HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND=
# Command syntax highlighting
source-plugin zsh-syntax-highlighting
(( ${+ZSH_HIGHLIGHT_STYLES} )) || typeset -A ZSH_HIGHLIGHT_STYLES
ZSH_HIGHLIGHT_STYLES[precommand]=fg=green
# Build system helper commands
source-plugin build
# Project sandboxing commands
source-plugin sandbox
# Various shell utilities
source-plugin utilities
# Automatically source .enter and .exit scripts on cd
source-plugin autoenv
# Session manager
source-plugin session
# Layout tmux window commands
[ "$TMUX" != "" ] && source-plugin layout
# Note taking commands
source-plugin notes
# Remove duplicates from history # Remove duplicates from history
setopt histignoredups setopt hist_ignore_all_dups
setopt hist_reduce_blanks
setopt hist_save_no_dups
# Enable multi-terminal history # Enable multi-terminal history
setopt share_history setopt inc_append_history
# Disable error when no glob matches are found
setopt nonomatch
# Enable comments in the prompt # Enable comments in the prompt
setopt interactive_comments setopt interactive_comments
@@ -66,30 +30,14 @@ setopt interactive_comments
setopt ignore_eof setopt ignore_eof
# Disable sound # Disable sound
setopt nobeep unsetopt beep
# Disable tty flow control, allows vim to use '<Ctrl>S' # Disable tty flow control, allows vim to use '<Ctrl>S'
setopt noflowcontrol; stty -ixon unsetopt flow_control && stty -ixon
# Enable completions in the middle of a word
setopt completeinword
# Initialize completions # Initialize completions
autoload -U compinit autoload -U compinit
compinit -d ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/compdump compinit
# Add pip to the old completion engine if present
if command -v pip &> /dev/null; then
function _pip_completion {
local words cword
read -Ac words
read -cn cword
reply=( $( COMP_WORDS="$words[*]" \
COMP_CWORD=$(( cword-1 )) \
PIP_AUTO_COMPLETE=1 $words[1] ) )
}
compctl -K _pip_completion pip
fi
# Enable prompt themes # Enable prompt themes
autoload -Uz promptinit autoload -Uz promptinit
@@ -99,15 +47,9 @@ prompt fresh
# Enable vi mode line editor keymap # Enable vi mode line editor keymap
bindkey -v bindkey -v
# Enable backspace after returning to viins from vicmd mode
bindkey '^?' backward-delete-char
# Enable yank, change, and delete whole line with 'Y', 'cc', and 'dd' # Enable yank, change, and delete whole line with 'Y', 'cc', and 'dd'
bindkey -M vicmd 'Y' vi-yank-whole-line bindkey -M vicmd 'Y' vi-yank-whole-line
# Edit the command line in vim
bindkey -M vicmd '^F' edit-command-line
# Enable undo with 'u' and redo with 'U' # Enable undo with 'u' and redo with 'U'
bindkey -M vicmd 'u' undo bindkey -M vicmd 'u' undo
bindkey -M vicmd 'U' redo bindkey -M vicmd 'U' redo
@@ -129,6 +71,9 @@ bindkey -M vicmd 'j' history-substring-search-down
bindkey -r '^[h' bindkey -r '^[h'
bindkey -M vicmd 'K' run-help bindkey -M vicmd 'K' run-help
# Disable Ex mode with ':'
bindkey -rM vicmd ':'
# Enable '<Shirt><Tab>' reverse order completions # Enable '<Shirt><Tab>' reverse order completions
bindkey '^[[Z' reverse-menu-complete bindkey '^[[Z' reverse-menu-complete
@@ -137,22 +82,17 @@ autoload -U edit-command-line
zle -N edit-command-line zle -N edit-command-line
bindkey -M vicmd '^V' edit-command-line bindkey -M vicmd '^V' edit-command-line
# Enable HOME and END keys # Get the shells parent process name.
if [[ `uname` = Linux ]]; then ppid_name() { echo $(ps -p $(ps -p $(echo $$) -o ppid=) -o comm=) }
# If Home and End are not working as expected setup zkbd mappings.
[ -f ~/.zkbd/$TERM-${${DISPLAY:t}:-$VENDOR-$OSTYPE} ] && \
source ~/.zkbd/$TERM-${${DISPLAY:t}:-$VENDOR-$OSTYPE}
[[ -n ${key[Home]} ]] && bindkey "${key[Home]}" beginning-of-line
[[ -n ${key[End]} ]] && bindkey "${key[End]}" end-of-line
fi
# Enable changing cursor shape based on vi mode # Enable changing cursor shape based on vi mode
if [ "$ITERM_PROFILE" != "" ] && [ "$TMUX" = "" ]; then if [ "$ITERM_PROFILE" != "" ] && [ "$TMUX" = "" ]; then
# iTerm2 cursor shape escape sequences outside tmux # iTerm2 cursor shape escape sequences outside tmux
cursor_block="\e]50;CursorShape=0\C-G" cursor_block="\e]50;CursorShape=0\C-G"
cursor_line="\e]50;CursorShape=1\C-G" cursor_line="\e]50;CursorShape=1\C-G"
else elif [ "$(ppid_name)" != "python2" ]; then
# iTerm2 inside tmux or VTE compatible cursor shape escape sequences. # iTerm2 inside tmux or VTE compatible cursor shape escape sequences,
# exclude Guake even though it's VTE based it doesn't like these
cursor_block="\e[2 q" cursor_block="\e[2 q"
cursor_line="\e[6 q" cursor_line="\e[6 q"
fi fi
@@ -177,70 +117,49 @@ if [[ ! -z "$cursor_block" && ! -z "$cursor_line" ]]; then
zle -N zle-line-finish zle -N zle-line-finish
fi fi
# Frequntly used directories # Remove duplicates from environment variables
function frequent-directory() { typeset -U PATH
if [ -d "$2" ]; then typeset -U MANPATH
# Replace - with _ in environment variable name. typeset -U INFOPATH
local name=${1//-/_}
local value=$2
export $name=$value
hash -d $1
fi
}
frequent-directory Desktop "$HOME/Desktop"
frequent-directory Documents "$HOME/Documents"
frequent-directory Downloads "$HOME/Downloads"
frequent-directory Projects "$HOME/Projects"
frequent-directory Sandbox "$HOME/Sandbox"
frequent-directory cache "${XDG_CACHE_HOME:-$HOME/.cache}"
frequent-directory config "${XDG_CONFIG_HOME:-$HOME/.config}"
frequent-directory local "$HOME/.local"
# Load work related config # Add ccache symlink directory to start of PATH, this must be after
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/work/zshrc ] && \ # `typeset -U PATH` because on macOS this reorders the list so ccache's
source ${XDG_CONFIG_HOME:-$HOME/.config}/work/zshrc # symlinks will no longer be at the start of PATH rendering it unusable.
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/private/zshrc ] && \ if [ `uname` = Darwin ]; then
source ${XDG_CONFIG_HOME:-$HOME/.config}/private/zshrc [ -f /usr/local/bin/ccache ] && \
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/local ] && \ PATH=/usr/local/opt/ccache/libexec:$PATH
source ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/local else
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/zshrc.local ] && \ [ -f /usr/bin/ccache ] && \
source ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/zshrc.local PATH=/usr/lib/ccache:$PATH
fi
# Aliases # Aliases
alias grep='grep --color=always' alias grep='grep --color=always'
command -v cmake &> /dev/null && \ which cmake &> /dev/null && \
alias cninja='cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON' alias cninja='cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMNADS=ON'
command -v ssh &> /dev/null && \ which ssh &> /dev/null && \
alias ssh='TERM=xterm-256color ssh' alias ssh='TERM=xterm-256color ssh'
alias weather="curl wttr.in"
alias cls="clear && printf '\e[3J'"
case `uname` in case `uname` in
Linux) Linux)
[ "$TMUX" = "" ] && \
alias cls="printf '\ec'" || \
alias cls="clear && printf '\e[3J'"
alias ls='ls -F --color=auto' alias ls='ls -F --color=auto'
if command -v cgdb &> /dev/null; then which cgdb &> /dev/null && \
alias debug='cgdb --args' alias debug='cgdb --args' || \
elif command -v gdb &> /dev/null; then which gdb &> /dev/null && \
alias debug='gdb --args' alias debug='gdb --args'
fi
;; ;;
Darwin) Darwin)
alias cls="clear && printf '\e[3J'"
alias ls='ls -GFh' alias ls='ls -GFh'
command -v lldb &> /dev/null && \ which lldb &> /dev/null && \
alias debug='lldb --' alias debug='lldb --'
;; ;;
esac esac
command -v wol > /dev/null && \ if [ "$TMUX" != "" ]; then
alias wakeonlan='wol' alias sp='tmux split-window'
alias vs='tmux split-window -h'
# Append any aliases to notify_ignore_list if they contain any ignore commands fi
# NOTE: Keep this at the end of ~/.zshrc so all aliases are processed
for name in ${(k)aliases}; do
cmd=${${${$(alias $name)#${name}=}#\'}#\"}
for ignore in $notify_ignore_list; do
if [[ "$cmd" = "$ignore"* ]]; then
notify_ignore_list+=( $name )
fi
done
done