26 Commits

Author SHA1 Message Date
256cd06e5a build-dir now exports build_dir envvar
In addition to setting the `~build` hashed directory, also export the
`build_dir` environment variable to enable commands like this:

```
cmake . -B$build_dir
```
2018-11-07 10:58:04 +00:00
9037d2dd41 Default to using VIM's termdebug plugin for debug
VIM 8.1 added the optional plugin termdebug which integrates gdb
with separate buffers for source, output, and gdb console. This adds the
`vimdebug` command which enables the termdebug plugin and executes the
`:TermdebugCommand` with the given arguments.
2018-11-07 10:53:14 +00:00
7b87688885 Use pinentry-curses for lastpass-cli on Debian 2018-10-31 17:33:16 +00:00
352a1c3f12 Fix git-prompt not resetting bold for untracked 2018-10-15 11:48:23 +01:00
8b099b103b Add ~/Projects hashed directory 2018-09-13 10:28:51 +01:00
9eee926c56 Move variable decl into for loop in git-prompt.c 2018-09-12 09:53:07 +01:00
29fb84b995 Disable GCC warning for fread in git-prompt.c 2018-09-12 09:48:44 +01:00
5f2c5b58da Move entire git-prompt to C 2018-09-11 20:56:27 +01:00
2740f9bc8d Add paging to sandbox list command 2018-09-05 20:37:10 +01:00
2589b63023 Fix build-dir not stooping on error, remove build-dir.py 2018-09-03 11:32:58 +01:00
6a4531423e Make virtualenv and docker prompt vars local 2018-09-01 10:39:09 +01:00
8819cd4d18 Make C-S work in vim again 2018-08-30 22:48:26 +01:00
585925df38 Fix vi-mode grips
Tips from [zsh-vimto](https://github.com/laurenkt/zsh-vimto).

* Remove delay when changing mode
* Enable backspace when returning to viins from vicmd mode
2018-08-30 22:44:22 +01:00
2ac97afdd1 Update options
Fix spellings of various options, enable some new options found in
[https://github.com/willghatch/zsh-saneopt](zsh-saneopt).
2018-08-30 22:30:11 +01:00
f24253b8c1 Remove use of RPS1
Following on from the previous commit, use of `RPS1` results in the
prompt being redrawn when the terminal gets narrower, this makes the
line drawn by `fresh_line_one` scroll off the screen. By removing any
use of `RPS1` this behaviour goes away.
2018-08-30 21:57:18 +01:00
df819f58a8 Update fresh prompt
* When a command fails, don't print the error code at the end of the
  first line but directly after the git stats.
* Remove `cr` from `prompt_opts` since there is no longer a new line in
  `PS1`, combined with above, this result in no new lines being inserted
  when the terminal becomes narrower e.g. a horizontal tmux split.
* Remove `fresh_visible_length` function and instead use the length of
  string variables e.g. `${#userhost}`.
2018-08-30 21:04:05 +01:00
c636dd078c Update sandbox to use .enter and .exit 2018-08-28 22:15:33 +01:00
0992b65d17 Don't explicitly add python to PATH
After fixing the macOS `/etc/zprofile` issue rewriting the `PATH`
environment variable it is no longer necessary to explicitly add
`/usr/local/opt/python/libexec/bin` to `PATH` since Homebrew installed
python 2.x is installed in `/usr/local/bin` and is used by default.
2018-08-28 21:58:22 +01:00
9e9c43a0cf Fix error in build-dir when --build is not set 2018-08-28 14:44:19 +01:00
0ab04406ec Fix macOS PATH being rewritten in /etc/zprofile
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.
2018-08-26 20:19:12 +01:00
e17ff47d5e Only load layout when inside tmux 2018-08-26 19:11:29 +01:00
ff8bb3e4ab Change layout path to ~/.local/share/tmux/layouts 2018-08-26 18:39:02 +01:00
31d06e0f19 Add sandbox directory color to fresh_line_one 2018-08-26 15:37:30 +01:00
7bb6a459ee Fix autoenv's use of zstat 2018-08-25 15:45:11 +01:00
9e4c984822 Fix chsh to work on macOS 2018-08-24 18:26:13 +01:00
9af452a8e2 Update readme file 2018-08-24 12:29:15 +01:00
13 changed files with 356 additions and 267 deletions

View File

@@ -3,11 +3,12 @@
- apt: - apt:
- zsh - zsh
- zsh-doc - zsh-doc
- pinentry-curses
- brew: - brew:
- zsh - zsh
- command: - command:
- install: sudo chsh $USER -s `which zsh` - install: sudo chsh -s `which zsh` $USER
remove: sudo chsh $USER -s `which bash` remove: sudo chsh -s `which bash` $USER
- symlink: - symlink:
- {src: zlogin, dst: ~/.zlogin} - {src: zlogin, dst: ~/.zlogin}
- {src: zlogout, dst: ~/.zlogout} - {src: zlogout, dst: ~/.zlogout}

View File

@@ -51,10 +51,26 @@ 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-syntax-highlighting][syntax] Fish shell like syntax highlighting for Zsh. * [zsh-autosuggestions][zsh-autosuggestions] Fish-like autosuggestions for zsh.
* [fast-syntax-highlighting][syntax] Optimized and extended
zsh-syntax-highlighting - 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
@@ -135,7 +151,9 @@ 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
[syntax]: https://github.com/zsh-users/zsh-syntax-highlighting [zsh-autosuggestions]: https://github.com/zdharma/fast-syntax-highlighting
[zsh-autoenv]: https://github.com/Tarrasch/zsh-autoenv
[syntax]: https://github.com/zdharma/fast-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
[tmux]: https://tmux.github.io [tmux]: https://tmux.github.io

View File

@@ -75,6 +75,9 @@ commands:
# 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,46 +0,0 @@
#!/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,7 +7,10 @@ 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 which cgdb &> /dev/null; then if [[ "`vim --version`" =~ "^VIM - Vi IMproved 8\.1.*$" ]]; then
function vimdebug() { vim "+packadd termdebug" "+TermdebugCommand $*" }
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'
@@ -37,31 +40,34 @@ optional arguments:
EOF EOF
return return
fi fi
error() { echo "\e[31merror:\e[0m $1"; return 1 } error() { echo "\e[31merror:\e[0m $1" }
local build_dir warning() { echo "\e[33mwarning:\e[0m $1" }
local local_build_dir
if [[ ${#*} -gt 1 ]]; then if [[ ${#*} -gt 1 ]]; then
echo $usage echo $usage
error "unexpected position arguments: ${*[2,${#*}]}" error "unexpected position arguments: ${*[2,${#*}]}"; return 1
elif [[ ${#*} -eq 1 ]]; then elif [[ ${#*} -eq 1 ]]; then
build_dir=${*[1]} if [[ ! -d ${*[1]} ]]; then
[[ ! -d $build_dir ]] && \ warning "directory not found: ${*[1]}"
error "directory not found: $build_dir" else
local_build_dir=${*[1]}
fi
fi fi
# If <directory> was not set begin selection # If <directory> was not set begin selection
if [[ -z $build_dir ]]; then if [[ -z $local_build_dir ]]; then
# Find build directories # Find build directories
local -a build_dirs local -a local_build_dirs
for entry in `ls -A`; do for entry in `ls -A`; do
[ -d $entry ] && [[ $entry =~ build* ]] && \ [ -d $entry ] && [[ $entry =~ build* ]] && \
build_dirs+=${entry/\//} local_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 [[ ${#build_dirs} -eq 0 ]]; then if [[ ${#local_build_dirs} -eq 0 ]]; then
error "no build directories found" error "no build directories found"; return 1
elif [[ ${#build_dirs} -gt 1 ]]; then elif [[ ${#local_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`
@@ -86,13 +92,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 < ${#build_dirs}; i++ )); do for (( i = 0; i < ${#local_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 ${build_dirs[$i+1]} zcurses string build-dir ${local_build_dirs[$i+1]}
done done
# Display the text the and wait for input # Display the text the and wait for input
@@ -104,7 +110,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 ${#build_dirs}-1 ]] && index=$index+1 ;; [[ $index -lt ${#local_build_dirs}-1 ]] && index=$index+1 ;;
(ENTER|$'\n') (ENTER|$'\n')
break ;; break ;;
esac esac
@@ -121,35 +127,38 @@ 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 $build_dir ]] && \ [[ -z $local_build_dir ]] && \
build_dir=${build_dirs[$index+1]} local_build_dir=${local_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 $build_dir/build.ninja ] && \ [ -f $local_build_dir/build.ninja ] && \
build="ninja -C $build_dir" build="ninja -C $local_build_dir"
# If `Makefile` exists in alias `make`, return. # If `Makefile` exists in alias `make`, return.
if [ -f $build_dir/Makefile ]; then if [ -f $local_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 $build_dir" build="make -j $cpu_count -C $local_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 [ -z $build ]; then
echo "\e[33mwarning:\e[0m build command detection failed: $build_dir" warning "build command detection failed: $local_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=$build_dir hash -d build=$local_build_dir
export build_dir=$local_build_dir
# If `--build` is specified then evaluate the command. # If `--build` is specified then evaluate the command.
[[ -n $do_build ]] && eval build if [[ -n $do_build ]]; then
eval build
fi
fi fi
} }

202
git-prompt.c Normal file
View File

@@ -0,0 +1,202 @@
#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 ~/.config/tmux/layouts 2>/dev/null | \ ls -1 ~/.local/share/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 ~/.config/tmux/layouts/$1 tmux source-file ~/.local/share/tmux/layouts/$1
if [[ "$2" != "" ]]; then if [[ "$2" != "" ]]; then
tmux rename-window $2 tmux rename-window $2
fi fi

View File

@@ -1,17 +1,18 @@
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
while getopts 'a' opt; do local recompile=0
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
@@ -19,8 +20,8 @@ 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 which can sometimes # actually part of the prompt so does not get redrawn, this is preferable
# cause lines before the prompt to disappear. # since sometimes lines before the prompt can 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
@@ -31,28 +32,29 @@ prompt_fresh_setup() {
add-zsh-hook -d preexec fresh_almostontop add-zsh-hook -d preexec fresh_almostontop
fi fi
if [ "$USERNAME" = "root" ]; then [ $recompile -eq 1 ] && prompt_cleanup
local user="%{%F{9}%}%n%{%f%}" fresh_compile_git_prompt
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" != "" ]; then
local user="%{%F{35}%}$user%{%f%}@%{%F{244}%}%M%{%f%}" local user="$user@%{%F{244}%}%M%{%f%}"
local userhost="$userhost@`hostname`"
fi fi
local length=`fresh_visible_length "$user"`
PS1="«$user» " PS1="«$user» "
PS2="${(l:$length + 1:: :)}» " PS2="«${(l:${#userhost}:: :)}» "
RPS1='$(fresh_rprompt)' prompt_opts=(percent sp subst)
prompt_opts=(cr percent sp subst)
} }
prompt_cleanup() { prompt_cleanup() {
if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi [ -f ~/.cache/zsh/git-prompt ] && rm ~/.cache/zsh/git-prompt
} }
fresh_line_one() { fresh_line_one() {
@@ -61,85 +63,30 @@ 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
if `git rev-parse --git-dir > /dev/null 2>&1`; then local git=`~/.cache/zsh/git-prompt`
# Get git branch name & exit early if not found
local branch="`git symbolic-ref HEAD 2> /dev/null`" # If the last command failed, display its error code at the right
if [ "" = "$branch" ]; then if [[ $exit_code -ne 0 ]]; then
local branch="`git rev-parse --abbrev-ref HEAD`" local result=" %{%B%F{1}%}$exit_code%{%f%b%}"
if [ "HEAD" = "$branch" ]; then
local branch="`git rev-parse --short $branch`"
fi
fi fi
if [ "$branch" != "" ]; then # If a virtualenv is enabled, display it's basename
local branch=${branch#refs/heads/} if [[ ! -z "$VIRTUAL_ENV" ]]; then
# Get the number of commits ahead/behind from the remote branch local py=" %{%F{4}%}py%{%f%}%{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
local remote="`git config branch.$branch.remote 2> /dev/null`"
if [ "" != "remote" ]; then
local branches="refs/remotes/$remote/$branch...HEAD"
local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
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
# Construct the git prompt # If docker-machine env is active, display the machines name
local git="%{%F{66}%}$branch%{%f%}$ahead$behind " if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
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
if [[ $exit_code -eq 0 ]]; then print -P "$time_stamp $directory$git$py$docker$result"
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
}
fresh_rprompt() {
rprompt=""
if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
rprompt="$rprompt %{%F{3}%}$DOCKER_MACHINE_NAME%{%f%}"
fi
if [[ ! -z "$VIRTUAL_ENV" ]]; then
rprompt="$rprompt %{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
fi
echo $rprompt
} }
fresh_almostontop() { fresh_almostontop() {
@@ -148,70 +95,15 @@ fresh_almostontop() {
print -P "$PROMPT"'$1' print -P "$PROMPT"'$1'
} }
fresh_visible_length() {
echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} ))
}
fresh_compile_git_prompt() { fresh_compile_git_prompt() {
# Compile a simple C executable which parses the output of `git status # Compile a simple C program which parses the output of `git status
# --procelain` to greatly decrease the time taken to render the prompt # --procelain` to greatly decrease the time taken to draw the prompt
local cache=~/.cache/zsh [ ! -d ~/.cache/zsh ] && mkdir -p ~/.cache/zsh
if [ ! -d $cache ]; then mkdir -p $cache; fi if [ ! -f ~/.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 ~/.config/zsh/git-prompt.c -o ~/.cache/zsh/git-prompt
#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 disabled, are the system development headers installed?" echo "git-prompt was not compiled, is a C99 toolchain installed?"
fi fi
fi fi
} }

View File

@@ -1,9 +1,3 @@
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
@@ -33,11 +27,11 @@ sandbox() {
begin=$PWD begin=$PWD
cd $sandbox cd $sandbox
echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> $SANDBOX_ENV_IN_FILE echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> .enter
echo "SANDBOX_NAME=$2" >> $SANDBOX_ENV_IN_FILE echo "SANDBOX_NAME=$2" >> .enter
echo "unset SANDBOX_NAME" >> $SANDBOX_ENV_OUT_FILE echo "unset SANDBOX_NAME" >> .exit
echo "unset SANDBOX_HOME" >> $SANDBOX_ENV_OUT_FILE echo "unset SANDBOX_HOME" >> .exit
git init &> /dev/null git init &> /dev/null
@@ -73,7 +67,7 @@ sandbox() {
rm -rf $sandbox rm -rf $sandbox
;; ;;
list) list)
/bin/ls -1 $SANDBOX_ROOT /bin/ls -1 $SANDBOX_ROOT | less -F -K -R -X
;; ;;
enable) enable)
if [[ "" == $2 ]]; then if [[ "" == $2 ]]; then

View File

@@ -1,2 +1,7 @@
# .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

29
zshenv
View File

@@ -5,22 +5,35 @@
# 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=5000 HISTSIZE=20000
SAVEHIST=5000 SAVEHIST=20000
# 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
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
# Remove duplicates from environment variables
typeset -U fpath
typeset -U PATH; export PATH
typeset -U MANPATH; export MANPATH
typeset -U INFOPATH; export INFOPATH
# 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
@@ -40,3 +53,7 @@ export GTEST_BREAK_ON_FAILURE=0
# Disable virtualenv prompt # Disable virtualenv prompt
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

40
zshrc
View File

@@ -32,18 +32,19 @@ source-plugin build
source-plugin sandbox source-plugin sandbox
# Layout tmux window commands # Layout tmux window commands
source-plugin layout [ "$TMUX" != "" ] && source-plugin layout
# Note taking commands # Note taking commands
source-plugin notes source-plugin notes
# Remove duplicates from history # Remove duplicates from history
setopt hist_ignore_all_dups setopt histignoredups
setopt hist_reduce_blanks
setopt hist_save_no_dups
# Enable multi-terminal history # Enable multi-terminal history
setopt inc_append_history setopt share_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
@@ -52,10 +53,13 @@ setopt interactive_comments
setopt ignore_eof setopt ignore_eof
# Disable sound # Disable sound
unsetopt beep setopt nobeep
# Disable tty flow control, allows vim to use '<Ctrl>S' # Disable tty flow control, allows vim to use '<Ctrl>S'
unsetopt flow_control && stty -ixon setopt noflowcontrol; stty -ixon
# Enable completions in the middle of a word
setopt completeinword
# Initialize completions # Initialize completions
autoload -U compinit autoload -U compinit
@@ -82,6 +86,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
@@ -158,22 +165,6 @@ fi
# 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 && \
@@ -200,3 +191,6 @@ case `uname` in
alias debug='lldb --' alias debug='lldb --'
;; ;;
esac esac
# Hashed Directories
hash -d Projects="$HOME/Projects"