1 Commits

Author SHA1 Message Date
0ce6c93faa Replace build-dir.py with zcurses build-dir
Update the build-dir shell function to usage zcurses instead of the
Python pick package to interactively select build directories, this
results in a more responsive user experience. Add argument parsing to
handle various use cases. Add prompt when the build command for the
selected build directory could not be detected, allowing the user to
specify the desired build command.

```
usage: build-dir [-h] [--build] [<directory>]

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
  --build     invoke a build after selection
```
2018-08-23 23:22:08 +01:00
22 changed files with 435 additions and 1031 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

31
.conduit.yaml Normal file
View File

@@ -0,0 +1,31 @@
---
- location: ~/.config/zsh
- apt:
- zsh
- zsh-doc
- brew:
- zsh
- command:
- install: sudo chsh $USER -s `which zsh`
remove: sudo chsh $USER -s `which bash`
- 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
- src: sandbox/_sandbox
dst: ~/.local/share/zsh/site-functions/_sandbox
- src: layout/_layout
dst: ~/.local/share/zsh/site-functions/_layout
- src: notes/_note
dst: ~/.local/share/zsh/site-functions/_note
- repo:
- https://github.com/zsh-users/zsh-autosuggestions.git
- https://github.com/zsh-users/zsh-history-substring-search.git
- https://github.com/zdharma/fast-syntax-highlighting.git
- pip:
- pick
- message: zsh will be the default prompt after next login

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
# Ignore all plugin files in subdirectories # Ignore all plugin files in subdirectories
*/ */*

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

@@ -16,8 +16,6 @@ _autoenv() {
init:'add .enter and .exit scripts in current directory' init:'add .enter and .exit scripts in current directory'
edit:'edit .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' deinit:'remove .enter and .exit scripts in current directory'
reload:'reload the current environment'
add=py:'add Python virtualenv to the autoenv'
) )
_describe -t commands command commands && ret=0 ;; _describe -t commands command commands && ret=0 ;;
esac esac

View File

@@ -12,7 +12,7 @@ autoenv() {
case "$cmd" in case "$cmd" in
-h|--help) # Display help. -h|--help) # Display help.
echo "\ echo "\
usage: autoenv [-h] {init,edit,deinit,reload,add=py} usage: autoenv [-h] {init,edit,deinit}
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
@@ -20,59 +20,40 @@ options:
commands: commands:
init add .enter and .exit scripts in current directory init add .enter and .exit scripts in current directory
edit edit .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 deinit remove .enter and .exit scripts in current directory"
reload reload the current environment
add=py add Python virtualenv to the autoenv"
;; ;;
init) # Create .enter and .exit scripts in current directory. init) # Create enter and exit scripts in current directory.
if [ -f $PWD/.enter ] || [ -f $PWD/.exit ]; then if [ -f $PWD/.enter ] || [ -f $PWD/.exit ]; then
echo '.enter or .exit already exists'; return 1 echo '.enter or .exit already exists'; return 1
fi fi
# Create the .enter and .exit scripts. # Create then edit enter and exit scripts.
touch .enter .exit touch .enter .exit && autoenv edit
# If enter script exists, authorize it. # If enter script exists, authorize it.
[ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes [ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes
# If exit script exists, authorize it. # If exit script exists, authorize it.
[ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes [ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes
# Edit the autoenv. # Enter the new autoenv.
autoenv edit _autoenv_enter $PWD ;;
# Enter the autoenv.
_autoenv_enter $PWD
;;
edit) # Edit .enter and .exit scripts in current directory. edit) # Edit enter and exit scripts in current directory.
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1 echo '.enter or .exit not found'; return 1
fi fi
# If vim exists, edit enter and exit scripts. # If vim exists, edit enter and exit scripts.
if which vim &> /dev/null; then if which vim &> /dev/null; then
# Exit the autoenv before editing. vim -p $PWD/.enter $PWD/.exit
_autoenv_exit $PWD
if vim -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
else else
echo 'vim not found'; return 1 echo 'vim not found'; return 1
fi fi ;;
;;
deinit) # Remove .enter and .exit scripts in current directory. deinit) # Remove enter and exit scripts in current directory.
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then 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. # Prompt user to confirm removal of enter and exit scripts.
while true; do while true; do
read "answer?Are you sure [y/N]? " read "answer?Are you sure [y/N]? "
case "$answer" in case "$answer" in
y|Y|yes) y|Y|yes)
# Exit the autoenv.
_autoenv_exit $PWD
# Remove enter and exit scripts if they exist. # Remove enter and exit scripts if they exist.
[ -f $PWD/.enter ] && rm $PWD/.enter [ -f $PWD/.enter ] && rm $PWD/.enter
[ -f $PWD/.exit ] && rm $PWD/.exit [ -f $PWD/.exit ] && rm $PWD/.exit
@@ -80,46 +61,20 @@ commands:
*) break ;; *) break ;;
esac esac
done done
;; else
echo '.enter and .exit not found'; return 1
reload) # Reload the current environment fi ;;
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=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 .local
echo 'source .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. *) # Invalid arguments, show help then error.
echo "invalid arguments: $@" echo "invalid arguments: $@"
autoenv --help autoenv --help
return 1 return 1 ;;
;;
esac esac
} }
# Global entered directories array. # Global entered directories array.
_autoenv_entered=() _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, # 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 # ignore, or view the file. Authorized files and their modified times are
# stored in the ~/.cache/autoenv/authorized file to make authorization # stored in the ~/.cache/autoenv/authorized file to make authorization

View File

@@ -1,6 +0,0 @@
#compdef build-dir
_arguments \
'(-h --help)'{-h,--help}'[]' \
'--build[invoke a build after selection]' \
'1:directory:_files'

46
build/build-dir.py Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python
"""The pick_build_dir command selects a build directory
The pick_build_dir command scans the current directory for directories starting
with ``build`` and prompts the user to select one from the list.
"""
from __future__ import print_function
from argparse import ArgumentParser
from os import listdir
from os.path import curdir, isdir
from sys import stderr
from pick import Picker
def main():
"""Main entry point to build-dir.py script."""
parser = ArgumentParser()
parser.add_argument('output')
parser.add_argument('--default', action='store_true')
args = parser.parse_args()
directories = []
for directory in listdir(curdir):
if isdir(directory) and directory.startswith('build'):
directories.append(directory)
if len(directories) == 0:
print('no build directories found', file=stderr)
exit(1)
build_dirs = sorted(directories)
if args.default:
build_dir = build_dirs[0]
else:
picker = Picker(build_dirs, 'Select a build directory:')
picker.register_custom_handler(ord(''), lambda _: exit(1))
build_dir, _ = picker.start()
with open(args.output, 'w') as output:
output.write(build_dir)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
exit(130)

View File

@@ -7,19 +7,7 @@ alias build="build-dir --build"
# Detect installed debugger and set the `debug` alias to debug a program with # Detect installed debugger and set the `debug` alias to debug a program with
# command line arguments. # command line arguments.
if [ `uname` = Linux ]; then if [ `uname` = Linux ]; then
if [[ "`vim --version|head -1|cut -c 19-21`" =~ "^8\.[123456789]$" ]]; then if which cgdb &> /dev/null; 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
vim "+packadd termdebug" "+TermdebugCommand $args"
}
alias debug='vimdebug'
elif which cgdb &> /dev/null; then
alias debug='cgdb --args' alias debug='cgdb --args'
elif which gdb &> /dev/null; then elif which gdb &> /dev/null; then
alias debug='gdb --args' alias debug='gdb --args'
@@ -47,36 +35,33 @@ optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
--build invoke a build after selection --build invoke a build after selection
EOF EOF
return exit
fi fi
error() { echo "\e[31merror:\e[0m $1" } error() { echo "\e[31merror:\e[0m $1"; exit 1 }
warning() { echo "\e[33mwarning:\e[0m $1" } local build_dir
local local_build_dir
if [[ ${#*} -gt 1 ]]; then if [[ ${#*} -gt 1 ]]; then
echo $usage echo $usage
error "unexpected position arguments: ${*[2,${#*}]}"; return 1 error "unexpected position arguments: ${*[2,${#*}]}"
elif [[ ${#*} -eq 1 ]]; then elif [[ ${#*} -eq 1 ]]; then
if [[ ! -d ${*[1]} ]]; then build_dir=${*[1]}
warning "directory not found: ${*[1]}" [[ ! -d $build_dir ]] && \
else error "directory not found: $build_dir"
local_build_dir=${*[1]}
fi
fi fi
# If <directory> was not set begin selection # If <directory> was not set begin selection
if [[ -z $local_build_dir ]]; then if [[ -z $build_dir ]]; then
# Find build directories # Find build directories
local -a local_build_dirs local -a build_dirs
for entry in `ls -A`; do for entry in `ls -A`; do
[ -d $entry ] && [[ $entry =~ build* ]] && \ [ -d $entry ] && [[ $entry =~ build* ]] && \
local_build_dirs+=${entry/\//} build_dirs+=$entry
done done
# Interactively select a build directory if more than 1 found # Interactively select a build directory if more than 1 found
integer index=0 integer index=0
if [[ ${#local_build_dirs} -eq 0 ]]; then if [[ ${#build_dirs} -eq 0 ]]; then
error "no build directories found"; return 1 error "no build directories found"
elif [[ ${#local_build_dirs} -gt 1 ]]; then elif [[ ${#build_dirs} -gt 1 ]]; then
zmodload zsh/curses && { zmodload zsh/curses && {
# Get the size of the terminal # Get the size of the terminal
local size=`stty size` local size=`stty size`
@@ -89,7 +74,7 @@ EOF
# Hide the cursor for zcurses, trap SIGINT to ensure cleanup in # Hide the cursor for zcurses, trap SIGINT to ensure cleanup in
# always-list occurs below # always-list occurs below
tput civis; trap 'return 130' INT tput civis; trap 'exit 130' INT
# Enter display loop # Enter display loop
local key keypad local key keypad
@@ -101,13 +86,13 @@ EOF
zcurses string build-dir 'Select a build directory:' zcurses string build-dir 'Select a build directory:'
# Add the selections text # Add the selections text
for (( i = 0; i < ${#local_build_dirs}; i++ )); do for (( i = 0; i < ${#build_dirs}; i++ )); do
integer line=$i+3 integer line=$i+3
zcurses move build-dir $line 1 zcurses move build-dir $line 1
[[ $index -eq $i ]] && [[ $index -eq $i ]] &&
zcurses string build-dir "* " || zcurses string build-dir "* " ||
zcurses string build-dir " " zcurses string build-dir " "
zcurses string build-dir ${local_build_dirs[$i+1]} zcurses string build-dir ${build_dirs[$i+1]}
done done
# Display the text the and wait for input # Display the text the and wait for input
@@ -119,7 +104,7 @@ EOF
(UP|k|$'\C-P') (UP|k|$'\C-P')
[[ $index -gt 0 ]] && index=$index-1 ;; [[ $index -gt 0 ]] && index=$index-1 ;;
(DOWN|j|$'\C-N') (DOWN|j|$'\C-N')
[[ $index -lt ${#local_build_dirs}-1 ]] && index=$index+1 ;; [[ $index -lt ${#build_dirs}-1 ]] && index=$index+1 ;;
(ENTER|$'\n') (ENTER|$'\n')
break ;; break ;;
esac esac
@@ -136,49 +121,46 @@ EOF
# On success setup the build directory for use # On success setup the build directory for use
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
# Set the build directory from selection if empty # Set the build directory from selection if empty
[[ -z $local_build_dir ]] && \ [[ -z $build_dir ]] && \
local_build_dir=${local_build_dirs[$index+1]} build_dir=${build_dirs[$index+1]}
# If `build.ninja` exists in alias `ninja`, return. # If `build.ninja` exists in alias `ninja`, return.
local build local build
[ -f $local_build_dir/build.ninja ] && \ [ -f $build_dir/build.ninja ] && \
build="ninja -C $local_build_dir" build="ninja -C $build_dir"
# If `Makefile` exists in alias `make`, return. # If `Makefile` exists in alias `make`, return.
if [ -f $local_build_dir/Makefile ]; then if [ -f $build_dir/Makefile ]; then
[ `uname` = Darwin ] && \ [ `uname` = Darwin ] && \
local cpu_count=`sysctl -n hw.ncpu` || local cpu_count=`sysctl -n hw.ncpu` ||
local cpu_count=`grep -c '^processor' /proc/cpuinfo` local cpu_count=`grep -c '^processor' /proc/cpuinfo`
build="make -j $cpu_count -C $local_build_dir" build="make -j $cpu_count -C $build_dir"
fi fi
# If the build variable is not defined the command could not be determined # If the build variable is not defined the command could not be determined
if [ -z $build ]; then if [ -n $build ]; then
warning "build command detection failed: $local_build_dir" echo "\e[33mwarning:\e[0m build command detection failed: $build_dir"
# Prompt user to enter a build command # Prompt user to enter a build command
vared -p 'enter comand: ' build vared -p 'enter comand: ' build
fi fi
# Redefine the `build` alias and update the `~build` hash directory # Redefine the `build` alias and update the `~build` hash directory
alias build="$build" alias build="$build"
hash -d build=$local_build_dir hash -d build=$build_dir
export build_dir=$local_build_dir
# If `--build` is specified then evaluate the command. # If `--build` is specified then evaluate the command.
if [[ -n $do_build ]]; then [[ -n $do_build ]] && eval build
eval build
fi
fi fi
} }
# Build then run a target residing in `~build/bin`. # Build then run a target residing in `~build/bin`.
build-run() { build-run() {
local target=$1; shift 1 local target=$1; shift 1
eval build $target && ~build/bin/$target "$@" eval build $target && ~build/bin/$target $*
} }
# Build then debug a target residing in `~build/bin`. # Build then debug a target residing in `~build/bin`.
build-debug() { build-debug() {
local target=$1; shift 1 local target=$1; shift 1
eval build $target && debug ~build/bin/$target "$@" eval build $target && debug ~build/bin/$target $*
} }

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,202 +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,7 +1,7 @@
#compdef layout #compdef layout
__get_layouts() { __get_layouts() {
ls -1 ~/.local/share/tmux/layouts 2>/dev/null | \ ls -1 ~/.config/tmux/layouts 2>/dev/null | \
while read -r layout; do echo $layout; done while read -r layout; do echo $layout; done
} }

View File

@@ -2,7 +2,7 @@ layout() {
if [[ "$1" == "" ]]; then if [[ "$1" == "" ]]; then
echo "usage: layout <layout> [name]" echo "usage: layout <layout> [name]"
else else
tmux source-file ~/.local/share/tmux/layouts/$1 tmux source-file ~/.config/tmux/layouts/$1
if [[ "$2" != "" ]]; then if [[ "$2" != "" ]]; then
tmux rename-window $2 tmux rename-window $2
fi 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,43 +19,40 @@ 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" != "" ] || [ "$DISTROBOX_ENTER_PATH" != "" ]; 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 ~/.cache/zsh/git-prompt ] && rm ~/.cache/zsh/git-prompt if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi
} }
fresh_line_one() { fresh_line_one() {
@@ -65,119 +61,157 @@ fresh_line_one() {
# 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{3}%}$SANDBOX_NAME${PWD#$SANDBOX_HOME}%{%f%}" || \
local directory="%{%F{37}%}%~%{%f%}" local directory="%{%F{37}%}%~%{%f%}"
# Check we are in a git repository # Check we are in a git repository
local git=`~/.cache/zsh/git-prompt` if `git rev-parse --git-dir > /dev/null 2>&1`; then
# Get git branch name & exit early if not found
# If the last command failed, display its error code at the right local branch="`git symbolic-ref HEAD 2> /dev/null`"
if [[ $exit_code -ne 0 ]]; then if [ "" = "$branch" ]; then
case $exit_code in local branch="`git rev-parse --abbrev-ref HEAD`"
129) exit_code="SIGHUP" ;; # 128 + 1 if [ "HEAD" = "$branch" ]; then
130) exit_code="SIGINT" ;; # 128 + 2 local branch="`git rev-parse --short $branch`"
131) exit_code="SIGQUIT" ;; # 128 + 3 fi
132) exit_code="SIGILL" ;; # 128 + 4
133) exit_code="SIGTRAP" ;; # 128 + 5
134) exit_code="SIGABRT" ;; # 128 + 6
134) exit_code="SIGIOT" ;; # 128 + 6
135) exit_code="SIGBUS" ;; # 128 + 7
136) exit_code="SIGFPE" ;; # 128 + 8
137) exit_code="SIGKILL" ;; # 128 + 9
138) exit_code="SIGUSR1" ;; # 128 + 10
139) exit_code="SIGSEGV" ;; # 128 + 11
140) exit_code="SIGUSR2" ;; # 128 + 12
141) exit_code="SIGPIPE" ;; # 128 + 13
142) exit_code="SIGALRM" ;; # 128 + 14
143) exit_code="SIGTERM" ;; # 128 + 15
144) exit_code="SIGSTKFLT" ;; # 128 + 16
145) exit_code="SIGCHLD" ;; # 128 + 17
146) exit_code="SIGCONT" ;; # 128 + 18
147) exit_code="SIGSTOP" ;; # 128 + 19
148) exit_code="SIGTSTP" ;; # 128 + 20
149) exit_code="SIGTTIN" ;; # 128 + 21
150) exit_code="SIGTTOU" ;; # 128 + 22
151) exit_code="SIGURG" ;; # 128 + 23
152) exit_code="SIGXCPU" ;; # 128 + 24
153) exit_code="SIGXFSZ" ;; # 128 + 25
154) exit_code="SIGVTALRM" ;; # 128 + 26
155) exit_code="SIGPROF" ;; # 128 + 27
156) exit_code="SIGWINCH" ;; # 128 + 28
157) exit_code="SIGIO" ;; # 128 + 29
158) exit_code="SIGPWR" ;; # 128 + 30
159) exit_code="SIGSYS" ;; # 128 + 31
esac
local result=" %{%B%F{1}%}$exit_code%{%f%b%}"
fi fi
# Unset vim/tmux navigate flag to handle C-z and multiple vim jobs. if [ "$branch" != "" ]; then
[[ ! -z "$TMUX" ]] && \ local branch=${branch#refs/heads/}
[[ "`tmux show-window-options`" = *"@vim$TMUX_PANE"* ]] && \ # Get the number of commits ahead/behind from the remote branch
tmux set-window-option -u @vim$TMUX_PANE local remote="`git config branch.$branch.remote 2> /dev/null`"
if [ "" != "remote" ]; then
# If a virtualenv is enabled, display it's basename local branches="refs/remotes/$remote/$branch...HEAD"
if [[ ! -z "$VIRTUAL_ENV" ]]; then local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
local py=" %{%F{4}%}py%{%f%}%{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}" if [ "0" -ne "$nahead" ]; then local ahead="↑$nahead"; fi
local nbehind=`git rev-list --left-only $branches --count 2> /dev/null`
if [ "0" -ne "$nbehind" ]; then local behind="↓$nbehind"; fi
fi fi
# If docker-machine env is active, display the machines name # Construct the git prompt
if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then local git="%{%F{66}%}$branch%{%f%}$ahead$behind "
local docker=" %{%F{6}%}$DOCKER_MACHINE_NAME%{%f%}"
if [ -f ~/.cache/zsh/git-prompt ]; then
# Get the change counts from git-prompt
local counts=(`~/.cache/zsh/git-prompt`)
# Parse the results from git-prompt
if [ 0 -eq ${#counts[@]} ]; then # clean
local git="$git%{%B%F{2}%}✓%{%f%b%}";
else
if [ 0 != $counts[1] ]; then # indexed
local git="$git%{%F{2}%}*$counts[1]%{%f%}"
fi fi
if [ 0 != $counts[2] ]; then # modified
local git="$git%{%F{1}%}+$counts[2]%{%f%}";
fi
if [ 0 != $counts[3] ]; then # deleted
local git="$git%{%F{1}%}-$counts[3]%{%f%}";
fi
if [ 0 != $counts[4] ]; then # unmerged
local git="$git%{%B%F{1}%}×$counts[4]%{%f%b%}";
fi
if [ 0 != $counts[5] ]; then # untracked
local git="$git%{%F{1}%}…%{%f%}";
fi
fi
fi
fi
fi
# Construct the prompt string
local line="$time_stamp $directory $git"
# 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 ~/.cache/zsh ] && mkdir -p ~/.cache/zsh local cache=~/.cache/zsh
if [ ! -f ~/.cache/zsh/git-prompt ]; then if [ ! -d $cache ]; then mkdir -p $cache; fi
cc -std=gnu99 -O3 -DNDEBUG -Wno-unused-result \ if [ ! -f $cache/git-prompt ]; then
~/.config/zsh/git-prompt.c -o ~/.cache/zsh/git-prompt cc -x c -std=gnu99 -O3 -DNDEBUG -o $cache/git-prompt - 2> /dev/null << EOF
#include <stdio.h>
#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,21 +1,16 @@
#compdef sandbox #compdef sandbox
__get_sandboxes() {
/bin/ls $SANDBOX_ROOT 2> /dev/null
}
__sandboxes() { __sandboxes() {
local -a sandboxes local -a sandboxes
sandboxes=(${(fo)"$(ls $SANDBOX_ROOT 2> /dev/null)"}) sandboxes=(${(fo)"$(__get_sandboxes)"})
_describe 'in' sandboxes _describe 'in' sandboxes
} }
_sandbox() { _sandbox_cmds() {
local context curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'1: :->cmd' \
'*:: :->args'
case $state in
(cmd)
local commands; commands=( local commands; commands=(
'create:Create a new sandbox' 'create:Create a new sandbox'
'rename:Rename an existing sandbox' 'rename:Rename an existing sandbox'
@@ -25,18 +20,27 @@ _sandbox() {
'disable:Disable the current sandbox' 'disable:Disable the current sandbox'
) )
_describe -t commands 'sandbox command' commands "$@" _describe -t commands 'sandbox command' commands "$@"
;; }
_sandbox() {
local context curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
'1: :_sandbox_cmds' \
'*::arg:->args'
case $state in
(args) (args)
curcontext="${curcontext%:*:*}:sandbox-cmd-$words[1]:" curcontext="${curcontext%:*:*}:sandbox-cmd-$words[1]:"
case $line[1] in case $line[1] in
(create) (create|list|disable)
_arguments -C '--git[repository to clone]: :'
;; ;;
(rename|enable|destroy) (enable|destroy)
_arguments -C '1:: :__sandboxes' _arguments -C '1:: :__sandboxes'
;; ;;
(list|disable) (rename)
_arguments -C '1:: :__sandboxes'
;; ;;
esac esac
esac esac

View File

@@ -1,133 +1,103 @@
if [[ "" == $SANDBOX_ENV_IN_FILE ]]; then
export SANDBOX_ENV_IN_FILE=$AUTOENV_IN_FILE
fi
if [[ "" == $SANDBOX_ENV_OUT_FILE ]]; then
export SANDBOX_ENV_OUT_FILE=$AUTOENV_OUT_FILE
fi
if [[ "" == $SANDBOX_ROOT ]]; then if [[ "" == $SANDBOX_ROOT ]]; then
export SANDBOX_ROOT=$HOME/Sandbox export SANDBOX_ROOT=$HOME/Sandbox
fi fi
sandbox() { sandbox() {
local usage="\ local usage="usage: sandbox {create,destroy,enable,disable} [name]"
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" } if [[ "" == $1 ]]; then
echo $usage
return 1
fi
local cmd=$1 case $1 in
[[ -z "$cmd" ]] && \
error "missing command\n$usage" && return 1
shift 1
case $cmd in
create) create)
# Parse command arguments. if [[ "" == $2 ]]; then
local git=false echo $usage
for arg in $@; do return 1
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 fi
else
if [ "$git" = true ]; then local sandbox=$SANDBOX_ROOT/$2
local repo=$arg if [[ -d $sandbox ]]; then
git=false echo "Sandbox '$2' already exists"
elif [[ -n "$name" ]]; then return 2
error "invalid argument $arg\n$usage" && return 1
else
local name=$arg
fi 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 mkdir -p $sandbox &> /dev/null
begin=$PWD
cd $sandbox cd $sandbox
echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> $SANDBOX_ENV_IN_FILE
echo "SANDBOX_NAME=$2" >> $SANDBOX_ENV_IN_FILE
echo "unset SANDBOX_NAME" >> $SANDBOX_ENV_OUT_FILE
echo "unset SANDBOX_HOME" >> $SANDBOX_ENV_OUT_FILE
git init &> /dev/null git init &> /dev/null
fi
echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> $sandbox/.enter cd $begin
echo "SANDBOX_NAME=$name" >> $sandbox/.enter cd $sandbox
_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) rename)
local old_name=$1 new_name=$2 if [[ "" == $2 || "" == $3 ]]; then
[[ -z "$old_name" ]] && \ echo $usage
error "missing argument <old-name>\n$usage" && return 1 return 1
[[ -z "$new_name" ]] && \ fi
error "missing argument <new-name>\n$usage" && return 1
local old=$SANDBOX_ROOT/$old_name new=$SANDBOX_ROOT/$new_name mv $SANDBOX_ROOT/$2 $SANDBOX_ROOT/$3
[[ ! -d "$old" ]] && \ sed -i "" "s/$2/$3/g" $SANDBOX_ROOT/$3/.env
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) destroy)
local name=$1 if [[ "" == $2 ]]; then
[[ -z "$name" ]] && \ echo $usage
error "missing argument <name>\n$usage" && return 1 return 1
fi
local sandbox=$SANDBOX_ROOT/$name local sandbox=$SANDBOX_ROOT/$2
[[ ! -d $sandbox ]] && \ if [[ ! -d $sandbox ]]; then
error "sandbox does not exist $name" && return 1 echo "Sandbox '$2' does not exist"
return 2
fi
[[ "$PWD" = "$sandbox"* ]] && cd ~ cd -
if [[ "${SANDBOX_ROOT##$PWD}" = "${SANDBOX_ROOT}" ]]; then
cd $HOME
fi
rm -rf $sandbox rm -rf $sandbox
;; ;;
list) list)
ls -1 $SANDBOX_ROOT | less -F -K -R -X /bin/ls -1 $SANDBOX_ROOT
;; ;;
enable) enable)
local name=$1 if [[ "" == $2 ]]; then
[[ -z "$name" ]] && \ echo $usage
error "missing argument <name>\n$usage" && return 1 return 1
fi
local sandbox=$SANDBOX_ROOT/$name local sandbox=$SANDBOX_ROOT/$2
[[ ! -d $sandbox ]] && \ if [[ ! -d $sandbox ]]; then
error "sandbox does not exist $name" && return 1 echo "Sandbox '$2' does not exist"
return 2
fi
export SANDBOX_RETURN=$PWD export SANDBOX_RETURN=$PWD
cd $sandbox cd $sandbox
;; ;;
disable) disable)
[[ -z "$SANDBOX_RETURN" ]] && \ if [[ -z $SANDBOX_RETURN ]]; then
error "sandbox is not currently active" && return 1 echo "Sandbox is not currently active"
return 2
fi
cd $SANDBOX_RETURN cd $SANDBOX_RETURN
unset SANDBOX_RETURN unset $SANDBOX_RETURN
;; ;;
esac esac
} }

View File

@@ -1,91 +0,0 @@
---
- name: zsh install packages
become: '{{package_become}}'
package:
name: zsh
state: present
- name: zsh install Debian packages
when: ansible_os_family == "Debian"
become: true
apt:
name:
- zsh-doc
- pinentry-curses
- unzip
state: present
- name: zsh clone plugin repos
git:
repo: '{{item.repo}}'
dest: '{{item.dest}}'
with_items:
- repo: https://github.com/zsh-users/zsh-autosuggestions.git
dest: ~/.config/zsh/zsh-autosuggestions
- repo: https://github.com/zsh-users/zsh-history-substring-search.git
dest: ~/.config/zsh/zsh-history-substring-search
- repo: https://github.com/zsh-users/zsh-syntax-highlighting.git
dest: ~/.config/zsh/zsh-syntax-highlighting
- repo: https://github.com/zsh-users/zsh-completions.git
dest: ~/.config/zsh/zsh-completions
- repo: https://github.com/junegunn/fzf.git
dest: ~/.config/zsh/fzf
- name: zsh install fzf binaries
command:
cmd: ~/.config/zsh/fzf/install --bin
creates: ~/.config/zsh/fzf/bin/fzf
- name: zsh create directories
file:
state: directory
dest: '{{item}}'
with_items:
- ~/.local/bin
- ~/.local/share/zsh/site-functions
- name: zsh create symbolic links
file:
state: link
src: '{{item.src}}'
dest: '{{item.dest}}'
with_items:
- src: ~/.config/zsh/zlogin
dest: ~/.zlogin
- src: ~/.config/zsh/zlogout
dest: ~/.zlogout
- src: ~/.config/zsh/zprofile
dest: ~/.zprofile
- src: ~/.config/zsh/zshenv
dest: ~/.zshenv
- src: ~/.config/zsh/zshrc
dest: ~/.zshrc
- src: ~/.config/zsh/prompt_fresh_setup
dest: ~/.local/share/zsh/site-functions/prompt_fresh_setup
- src: ~/.config/zsh/build/_build-dir
dest: ~/.local/share/zsh/site-functions/_build-dir
- src: ~/.config/zsh/sandbox/_sandbox
dest: ~/.local/share/zsh/site-functions/_sandbox
- src: ~/.config/zsh/layout/_layout
dest: ~/.local/share/zsh/site-functions/_layout
- src: ~/.config/zsh/notes/_note
dest: ~/.local/share/zsh/site-functions/_note
- src: ~/.config/zsh/fzf/bin/fzf
dest: ~/.local/bin/fzf
- src: ~/.config/zsh/fzf/bin/fzf-tmux
dest: ~/.local/bin/fzf-tmux
- src: ~/.config/zsh/cmake-uninstall
dest: ~/.local/bin/cmake-uninstall
- src: ~/.config/zsh/$
dest: ~/.local/bin/$
- name: zsh get absolute path
shell: command -v zsh
register: zsh
changed_when: false
- name: zsh set default shell
user:
name: '{{lookup("env", "USER")}}'
shell: '{{zsh.stdout}}'
become: true

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env zsh
# Check if third party completions are present.
local zsh_completions=~/.config/zsh/zsh-completions
[ ! -d $zsh_completions ] && return 0
# Loop over all completions.
for completion in $zsh_completions/src/_*; do
local name=`basename $completion`
local symlink=~/.local/share/zsh/site-functions/$name
# Remove existing completion file if it exists.
[ -f $symlink ] && rm $symlink
# Check if the command exists on the PATH.
if which ${name:1} &> /dev/null; then
# Symlink the completion for the existing command.
[ `uname` = Darwin ] && \
ln -s $completion $symlink || ln -sr $completion $symlink
fi
done

View File

@@ -1,107 +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 [ -n "$SSH_CONNECTION" ]; then
# Use OSC-52 to get the clipboard
alias paste='printf "\033]52;c;?\a"'
elif [ "`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() {
if [ -n "$TMUX" ]; then
printf "\x1bPtmux;\x1b$1\x1b\\"
else
printf "$1"
fi
}
# OSC 9 - Post a notification - supported by iTerm2, maybe others?
notify() {
tmux-dcs-passthrough "\x1b]9;$*\x7"
}
# 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

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

74
zshenv
View File

@@ -5,51 +5,22 @@
# Enable saving command history to file # Enable saving command history to file
[ ! -d $HOME/.cache/zsh ] && mkdir -p $HOME/.cache/zsh [ ! -d $HOME/.cache/zsh ] && mkdir -p $HOME/.cache/zsh
HISTFILE=$HOME/.cache/zsh/histfile HISTFILE=$HOME/.cache/zsh/histfile
HISTSIZE=20000 HISTSIZE=5000
SAVEHIST=20000 SAVEHIST=5000
# 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 Homebrew python to PATH on macOS if present
[ "`uname`" = "Darwin" ] && [ -d /usr/local/opt/python/libexec/bin ] &&
PATH=/usr/local/opt/python/libexec/bin:$PATH
# Add ~/.local to the environment # Add ~/.local to the environment
fpath+=$HOME/.local/share/zsh/site-functions fpath+=$HOME/.local/share/zsh/site-functions
PATH=$HOME/.local/bin:$PATH 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 ccache compiler aliases to PATH and use XDG base dir paths
if [ `uname` = Darwin ]; then
[ -d /usr/local/opt/python/libexec/bin ] && \
PATH=/usr/local/opt/python/libexec/bin:$PATH
[ -f /usr/local/bin/ccache ] && \
PATH=/usr/local/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=$HOME/.config/ccache
export CCACHE_DIR=$HOME/.cache/ccache
# Add default CMake generator
which ninja &> /dev/null && \
export CMAKE_GENERATOR=Ninja
# 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.
which vim &> /dev/null && \
export EDITOR=`which vim`
# 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
@@ -61,42 +32,11 @@ 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 # Force GoogleTest to output colors
export GTEST_COLOR=yes export GTEST_COLOR=yes
# Allow completions for GoogleTest break on failure # Allow completions for GoogleTest break on failure
export GTEST_BREAK_ON_FAILURE=0 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
which pinentry-curses &> /dev/null && \
export LPASS_PINENTRY=pinentry-curses
# Teach these some XDG Base Directory Spec manners
export IPYTHONDIR=$HOME/.config/ipython
which cargo &> /dev/null && \
export CARGO_HOME=$HOME/.local/share/cargo
if which ccache &> /dev/null; then
export CCACHE_CONFIGPATH=$HOME/.config/ccache.conf
export CCACHE_DIR=$HOME/.cache/ccache
fi
which conan &> /dev/null && \
export CONAN_USER_HOME=$HOME/.local/share/conan
which docker &> /dev/null && \
export DOCKER_CONFIG=$HOME/.local/share/docker
export GTK_RC_FILES=$HOME/.config/gtk/gtkrc
export GTK2_RC_FILES=$HOME/.config/gtk-2.0/gtkrc
which rustup &> /dev/null && \
export RUSTUP_HOME=$HOME/.local/share/rustup
export PYLINTHOME=$HOME/.cache/pylint
# TODO: terminfo
[ -f $HOME/.config/wgetrc ] &&
export WGETRC=$HOME/.config/wgetrc

74
zshrc
View File

@@ -19,7 +19,11 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(end-of-line vi-end-of-line)
source-plugin zsh-history-substring-search source-plugin zsh-history-substring-search
# Command syntax highlighting # Command syntax highlighting
source-plugin zsh-syntax-highlighting source-plugin fast-syntax-highlighting
fast-theme -q ~/.config/zsh/fresh.ini
# Automatically source .enter and .exit scripts on cd
source-plugin autoenv
# Build system helper commands # Build system helper commands
source-plugin build source-plugin build
@@ -27,26 +31,19 @@ source-plugin build
# Project sandboxing commands # Project sandboxing commands
source-plugin sandbox source-plugin sandbox
# Various shell utilities
source-plugin utilities
# Automatically source .enter and .exit scripts on cd
source-plugin autoenv
# Layout tmux window commands # Layout tmux window commands
[ "$TMUX" != "" ] && source-plugin layout source-plugin layout
# Note taking commands # Note taking commands
source-plugin notes 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
@@ -55,13 +52,10 @@ 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
@@ -88,9 +82,6 @@ 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
@@ -129,22 +120,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
@@ -169,24 +155,37 @@ if [[ ! -z "$cursor_block" && ! -z "$cursor_line" ]]; then
zle -N zle-line-finish zle -N zle-line-finish
fi fi
# Frequntly used directories
function frequent-directory() { export $1; hash -d $1 }
frequent-directory Projects="$HOME/Projects"
# Load work related config # Load work related config
[ -f ~/.config/work/zshrc ] && source ~/.config/work/zshrc [ -f ~/.config/work/zshrc ] && source ~/.config/work/zshrc
# Remove duplicates from environment variables
typeset -U PATH
typeset -U MANPATH
typeset -U INFOPATH
# Add ccache symlink directory to start of PATH, this must be after
# `typeset -U PATH` because on macOS this reorders the list so ccache's
# symlinks will no longer be at the start of PATH rendering it unusable.
if [ `uname` = Darwin ]; then
[ -f /usr/local/bin/ccache ] && \
PATH=/usr/local/opt/ccache/libexec:$PATH
else
[ -f /usr/bin/ccache ] && \
PATH=/usr/lib/ccache:$PATH
fi
# Aliases # Aliases
alias grep='grep --color=always' alias grep='grep --color=always'
which cmake &> /dev/null && \ which cmake &> /dev/null && \
alias cninja='cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON' alias cninja='cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON'
which 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 which cgdb &> /dev/null; then if which cgdb &> /dev/null; then
alias debug='cgdb --args' alias debug='cgdb --args'
@@ -195,6 +194,7 @@ case `uname` in
fi fi
;; ;;
Darwin) Darwin)
alias cls="clear && printf '\e[3J'"
alias ls='ls -GFh' alias ls='ls -GFh'
which lldb &> /dev/null && \ which lldb &> /dev/null && \
alias debug='lldb --' alias debug='lldb --'