1 Commits

Author SHA1 Message Date
fff22c22d7 Fix bug in almostontop that truncated long prompts
Add collection of cursor line immediately after the previous command
completes and use this when calculating the number of lines required to
move the screen in order to display only the latest prompt.
2021-12-07 16:56:08 +00:00
22 changed files with 419 additions and 679 deletions

4
.gitignore vendored
View File

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

View File

@@ -17,7 +17,6 @@ _autoenv() {
edit:'edit .enter and .exit scripts in current directory'
deinit:'remove .enter and .exit scripts in current directory'
reload:'reload the current environment'
add=local:'add .local/bin to PATH'
add=py:'add Python virtualenv to the autoenv'
)
_describe -t commands command commands && ret=0 ;;

View File

@@ -22,7 +22,6 @@ commands:
edit edit .enter and .exit scripts in current directory
deinit remove .enter and .exit scripts in current directory
reload reload the current environment
add=local add .local/bin to PATH
add=py add Python virtualenv to the autoenv"
;;
@@ -36,6 +35,8 @@ commands:
[ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes
# If exit script exists, authorize it.
[ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes
# Edit the autoenv.
autoenv edit
# Enter the autoenv.
_autoenv_enter $PWD
;;
@@ -44,9 +45,11 @@ commands:
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
# If vim exists, edit enter and exit scripts.
if which vim &> /dev/null; then
# Exit the autoenv before editing.
_autoenv_exit $PWD
if $EDITOR -p $PWD/.enter $PWD/.exit; then
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.
@@ -54,6 +57,9 @@ commands:
fi
# Enter the autoenv.
_autoenv_enter $PWD
else
echo 'vim not found'; return 1
fi
;;
deinit) # Remove .enter and .exit scripts in current directory.
@@ -86,34 +92,13 @@ commands:
_autoenv_enter $PWD
;;
add=local) # Add .local/bin to PATH
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
_autoenv_exit $PWD
# Create .local/bin if not present
if ! [ -d $PWD/.local/bin ]; then
mkdir -p $PWD/.local/bin
fi
# On enter: store PATH and insert .local/bin
echo 'OLDPATH=$PATH' >> .enter
echo 'PATH=$PWD/.local/bin:$PATH' >> .enter
# On exit: reset PATH
echo 'PATH=$OLDPATH' >> .exit
echo 'unset OLDPATH' >> .exit
# Authorize modified autoenv
_autoenv_authorized $PWD/.enter yes
_autoenv_authorized $PWD/.exit yes
_autoenv_enter $PWD
;;
add=py) # Add Python virtualenv to the sandbox
if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then
echo '.enter or .exit not found'; return 1
fi
_autoenv_exit $PWD
virtualenv -p `command -v python` .local
echo 'source ${0:a:h}/.local/bin/activate' >> .enter
virtualenv .local
echo 'source .local/bin/activate' >> .enter
echo 'deactivate' >> .exit
_autoenv_authorized $PWD/.enter yes
_autoenv_authorized $PWD/.exit yes
@@ -137,22 +122,16 @@ zmodload -F zsh/stat b:zstat
# Check if the given file is authorized, if not prompt the user to authorize,
# ignore, or view the file. Authorized files and their modified times are
# stored in the $XDG_STATE_HOME/autoenv/authorized file to make authorization
# stored in the ~/.cache/autoenv/authorized file to make authorization
# persistent.
_autoenv_authorized() {
local file=$1 yes=$2
# If autoenv state directory does not exist, create it.
! [ -d ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv ] && \
mkdir -p ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv
# Migrate from cache to state directory
[ -f $HOME/.cache/autoenv/authorized ] && \
mv $HOME/.cache/autoenv/authorized \
${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
# If autoenv cache directory does not exist, create it.
! [ -d ~/.cache/autoenv ] && mkdir -p ~/.cache/autoenv
# If the authorized file does not exist, create it.
! [ -f ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized ] && \
touch ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
! [ -f ~/.cache/autoenv/authorized ] && touch ~/.cache/autoenv/authorized
# Load the authorized file into a map of authorized key value pairs.
typeset -A authorized=(`cat ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized`)
typeset -A authorized=(`cat ~/.cache/autoenv/authorized`)
# If the file has been removed, return.
! [ -f $file ] && return 1
# If the given file has been authorized, i.e. the modified time matches that
@@ -174,7 +153,7 @@ _autoenv_authorized() {
# Add file to the authorized map.
authorized[$file]=$modified_time
# Store authorized map in authorized file.
echo ${(kv)authorized} > ${XDG_STATE_HOME:-$HOME/.local/state}/autoenv/authorized
echo ${(kv)authorized} > ~/.cache/autoenv/authorized
}
# Source an enter script and add its directory to the global entered

View File

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

View File

@@ -7,6 +7,7 @@ alias build="build-dir --build"
# Detect installed debugger and set the `debug` alias to debug a program with
# command line arguments.
if [ `uname` = Linux ]; then
if [[ "`vim --version|head -1|cut -c 19-21`" =~ "^8\.[123456789]$" ]]; then
autoload -U regexp-replace
function vimdebug() {
# For each item in $* replace * and \* and then replace \ with \\
@@ -15,23 +16,24 @@ if [ `uname` = Linux ]; then
regexp-replace arg '\*' '\\*'
args+=($arg)
done
nvim "+packadd termdebug" "+TermdebugCommand $args"
vim "+packadd termdebug" "+TermdebugCommand $args"
}
if command -v nvim &> /dev/null; then
alias debug=vimdebug
elif command -v gdb &> /dev/null; then
alias debug='vimdebug'
elif which cgdb &> /dev/null; then
alias debug='cgdb --args'
elif which gdb &> /dev/null; then
alias debug='gdb --args'
fi
elif [ `uname` = Darwin ]; then
command -v lldb &> /dev/null && \
which lldb &> /dev/null && \
alias debug='lldb --'
fi
# Interactively choose a `~build` directory for `build` to build.
build-dir() {
local usage='usage: build-dir [-h] [-s] [--build] [<directory>]'
local -a help show do_build
zparseopts -D h=help -help=help s=show -show=show -build=do_build
local usage='usage: build-dir [-h] [--build] [<directory>]'
local -a help do_build
zparseopts -D h=help -help=help -build=do_build
if [[ -n $help ]]; then
cat << EOF
$usage
@@ -43,26 +45,16 @@ positional arguments:
optional arguments:
-h, --help show this help message and exit
-s, --show show the current build directory
--build invoke a build after selection
EOF
return
fi
error() { echo "\e[31merror:\e[0m $1" }
warning() { echo "\e[33mwarning:\e[0m $1" }
if [[ -n $show ]]; then
if [[ ! -n $build_dir ]]; then
error "build directory not set"
return 1
else
echo "$build_dir"
return
fi
fi
local local_build_dir
if [[ ${#*} -gt 1 ]]; then
echo $usage
error "unexpected positional arguments: ${*[2,${#*}]}"; return 1
error "unexpected position arguments: ${*[2,${#*}]}"; return 1
elif [[ ${#*} -eq 1 ]]; then
if [[ ! -d ${*[1]} ]]; then
warning "directory not found: ${*[1]}"
@@ -84,23 +76,69 @@ EOF
integer index=0
if [[ ${#local_build_dirs} -eq 0 ]]; then
error "no build directories found"; return 1
elif [[ ${#local_build_dirs} -eq 1 ]]; then
local_build_dir=${local_build_dirs[1]}
elif [[ ${#local_build_dirs} -gt 1 ]]; then
# Use fzf to select a build directory
local max=$(( $( tput lines ) / 2 ))
local best=$(( ${#local_build_dirs} + 4 ))
local_build_dir=$(
printf '%s\n' "${local_build_dirs[@]}" |
fzf --layout=reverse --tac --info=hidden --border=rounded \
--cycle --height=$(( $best < $max ? $best : $max ))
)
if [[ $? -ne 0 ]]; then
return 1
fi
zmodload zsh/curses && {
# Get the size of the terminal
local size=`stty size`
integer height=${size% *}
integer width=${size#* }
# Create the window and hide the cursor
zcurses init
zcurses addwin build-dir $height $width 0 0
# Hide the cursor for zcurses, trap SIGINT to ensure cleanup in
# always-list occurs below
tput civis; trap 'return 130' INT
# Enter display loop
local key keypad
while (( 1 )); do
zcurses clear build-dir
# Add the prompt text
zcurses move build-dir 1 1
zcurses string build-dir 'Select a build directory:'
# Add the selections text
for (( i = 0; i < ${#local_build_dirs}; i++ )); do
integer line=$i+3
zcurses move build-dir $line 1
[[ $index -eq $i ]] &&
zcurses string build-dir "* " ||
zcurses string build-dir " "
zcurses string build-dir ${local_build_dirs[$i+1]}
done
# Display the text the and wait for input
zcurses refresh build-dir
zcurses input build-dir key keypad
# Handle user input
case $key in
(UP|k|$'\C-P')
[[ $index -gt 0 ]] && index=$index-1 ;;
(DOWN|j|$'\C-N')
[[ $index -lt ${#local_build_dirs}-1 ]] && index=$index+1 ;;
(ENTER|$'\n')
break ;;
esac
done
} always {
# Restore the cursor and cleanup zcurses
tput cvvis; tput cnorm
zcurses delwin build-dir
zcurses end
}
fi
fi
# On success setup the build directory for use
if [[ $? -eq 0 ]]; then
# Set the build directory from selection if empty
[[ -z $local_build_dir ]] && \
local_build_dir=${local_build_dirs[$index+1]}
# If `build.ninja` exists in alias `ninja`, return.
local build
[ -f $local_build_dir/build.ninja ] && \
@@ -125,17 +163,12 @@ EOF
alias build="$build"
hash -d build=$local_build_dir
export build_dir=$local_build_dir
export BUILD_DIR=$PWD/$local_build_dir
echo "$build_dir"
# If `--build` is specified then evaluate the command.
if [[ -n $do_build ]]; then
eval build
fi
# Bind C-B to fuzzy find & complete cmake variables.
zle -N .build-var
bindkey '^B' .build-var
fi
}
# Build then run a target residing in `~build/bin`.
@@ -149,22 +182,3 @@ build-debug() {
local target=$1; shift 1
eval build $target && debug ~build/bin/$target "$@"
}
# Fuzzy find CMake variables, select one to set the variable via a command.
.build-var() {
local var=$(
cat $build_dir/CMakeCache.txt |
grep --color=never -Ex '^\w+:\w+=.*$' |
fzf --layout=reverse --info=hidden --border=rounded \
--cycle --height=50%
)
if [[ -n "$var" ]]; then
if [[ "$BUFFER" = "cmake"* ]]; then
BUFFER="$BUFFER-D$var"
else
BUFFER="cmake -B\$build_dir -D$var"
fi
zle end-of-line
fi
zle reset-prompt
}

76
fresh.ini Normal file
View File

@@ -0,0 +1,76 @@
; fresh fast-syntax-highlighting theme
[base]
default = none
unknown-token = red,bold
commandseparator = none
redirection = none
here-string-tri = yellow
here-string-word = bg:blue
exec-descriptor = yellow,bold
comment = black,bold
correct-subtle = none
incorrect-subtle = red
subtle-bg = bg:blue
secondary =
[command-point]
reserved-word = yellow
alias = green
suffix-alias = green
global-alias = bg:blue
builtin = green
function = green
command = green
precommand = green
hashed-command = green
[paths]
path = none
pathseparator = none
path-to-dir = none
globbing = blue,bold
[brackets]
paired-bracket = bg:blue
bracket-level-1 = green,bold
bracket-level-2 = yellow,bold
bracket-level-3 = cyan,bold
[arguments]
single-hyphen-option = cyan
double-hyphen-option = cyan
back-quoted-argument = none
single-quoted-argument = yellow
double-quoted-argument = yellow
dollar-quoted-argument = yellow
[in-string]
; backslash in $'...'
back-dollar-quoted-argument = cyan
; backslash or $... in "..."
back-or-dollar-double-quoted-argument = cyan
[other]
variable = 113
assign = none
assign-array-bracket = green
history-expansion = blue,bold
[math]
mathvar = blue,bold
mathnum = magenta
matherr = red
[for-loop]
forvar = none
fornum = magenta
; operator
foroper = yellow
; separator
forsep = yellow,bold
[case]
case-input = green
case-parentheses = yellow
case-condition = bg:blue

View File

@@ -56,9 +56,7 @@ int process_close(process_t process) {
char* trim(char* str) {
char* end;
while (isspace((unsigned char)*str)) {
str++;
}
while (isspace((unsigned char)*str)) str++;
if (*str == 0) {
return str;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,40 +44,33 @@ prompt_fresh_setup() {
fi
local userhost=$USER
if [ "$SSH_CONNECTION" != "" ] || [ "$container" != "" ]; then
if [ "$SSH_CONNECTION" != "" ]; then
local user="$user@%{%F{244}%}%M%{%f%}"
local userhost="$userhost@`hostname`"
fi
PS1="$user "
PS2="${(l:${#userhost}:: :)} "
PS1="«$user» "
PS2="«${(l:${#userhost}:: :)}» "
prompt_opts=(percent sp subst)
}
prompt_cleanup() {
[ -f ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt ] && \
rm ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt
[ -f ~/.cache/zsh/git-prompt ] && rm ~/.cache/zsh/git-prompt
}
fresh_line_one() {
# First get the last commands exit code before doing anything
local exit_code=$?
# Clean up if fresh is no longer the current prompt theme
if [[ "`prompt -c | tail -1 | xargs`" != "fresh"* ]]; then
add-zsh-hook -d precmd fresh_line_one
return
fi
# Construct the time and directory portions of the prompt
local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}"
[[ -n $SANDBOX_HOME ]] && \
local directory="%{%F{220}%}$SANDBOX_NAME${PWD#$SANDBOX_HOME}%{%f%}" || \
local directory="%{%F{3}%}$SANDBOX_NAME${PWD#$SANDBOX_HOME}%{%f%}" || \
local directory="%{%F{37}%}%~%{%f%}"
# Check we are in a git repository
local git=`${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt`
local git=`~/.cache/zsh/git-prompt`
# If the last command failed, display its error code at the right
if [[ $exit_code -ne 0 ]]; then
@@ -139,14 +132,16 @@ fresh_line_one() {
# Executed before each prompt.
fresh_almostontop_precmd() {
# TODO: store the cursor position for use in fresh_almostontop_preexec
# CSI ESC[6n gets the cursor position in the form ESC[<row>;<column>R
printf "\033[6n"
# Discard prefix delimited by [
read -s -d [
# Store the <row> delimited by ; in row_before
read -s -d \; row_before
# Store the <row> delimited by ; in fresh_precmd_row
read -s -d \; fresh_precmd_row
# Discard suffix delimted by R otherwise it is output to the tty
read -s -d R
# printf "$fresh_precmd_row\n"
}
# Executed just after a command has been read and is about to be executed.
@@ -164,7 +159,7 @@ fresh_almostontop_preexec() {
let "down = $LINES - $row"
printf "\033[${down}B"
# Calculate the number of lines in the prompt
let "prompt_lines = ($row - $row_before) + 2"
let "prompt_lines = ($row - $fresh_precmd_row) + 2"
# Print new lines to push the old command out of view
let "new = $row - $prompt_lines"
for (( i = 0; i < $new; i++ )); do
@@ -179,12 +174,10 @@ fresh_almostontop_preexec() {
fresh_compile_git_prompt() {
# Compile a simple C program which parses the output of `git status
# --procelain` to greatly decrease the time taken to draw the prompt
[ ! -d ${XDG_CACHE_HOME:-$HOME/.cache}/zsh ] && \
mkdir -p ${XDG_CACHE_HOME:-$HOME/.cache}/zsh
if [ ! -f ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt ]; then
[ ! -d ~/.cache/zsh ] && mkdir -p ~/.cache/zsh
if [ ! -f ~/.cache/zsh/git-prompt ]; then
cc -std=gnu99 -O3 -DNDEBUG -Wno-unused-result \
${XDG_CONFIG_HOME:-$HOME/.config}/zsh/git-prompt.c \
-o ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/git-prompt
~/.config/zsh/git-prompt.c -o ~/.cache/zsh/git-prompt
if [ $? -ne 0 ]; then
echo "git-prompt was not compiled, is a C99 toolchain installed?"
fi

View File

@@ -36,7 +36,7 @@ usage: sandbox [-h] {create,rename,destroy,enable,disable,list} ..
if [ "$git" = true ]; then
local repo=$arg
git=false
elif [[ -z "$name" ]]; then
elif [[ -n "$name" ]]; then
error "invalid argument $arg\n$usage" && return 1
else
local name=$arg
@@ -44,11 +44,14 @@ usage: sandbox [-h] {create,rename,destroy,enable,disable,list} ..
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
@@ -58,49 +61,55 @@ usage: sandbox [-h] {create,rename,destroy,enable,disable,list} ..
cd $sandbox
git init &> /dev/null
fi
echo "SANDBOX_HOME=\$(dirname -- "\$0:a")" >> $sandbox/.enter
echo "SANDBOX_NAME=$name" >> $sandbox/.enter
_autoenv_authorized $sandbox/.enter yes
echo "unset SANDBOX_NAME" >> $sandbox/.exit
echo "unset SANDBOX_HOME" >> $sandbox/.exit
_autoenv_authorized $sandbox/.exit yes
_autoenv_enter $sandbox
;;
rename)
local old_name=$1 new_name=$2
[[ -z "$old_name" ]] && \
error "missing argument <old-name>\n$usage" && return 1
[[ -z "$new_name" ]] && \
error "missing argument <new-name>\n$usage" && return 1
local old=$SANDBOX_ROOT/$old_name new=$SANDBOX_ROOT/$new_name
[[ ! -d "$old" ]] && \
error "sandbox does not exist $old_name" && return 1
[[ -d "$new" ]] && \
error "sandbox already exists $new_name" && return 1
[[ "$PWD" = "$old"* ]] && _autoenv_exit $PWD
mv $old $new
sed -i "s/$old_name/$new_name/g" $new/.enter
_autoenv_authorized $new/.enter yes
_autoenv_authorized $new/.exit yes
[[ "$PWD" = "$old"* ]] && cd $new
;;
destroy)
local name=$1
[[ -z "$name" ]] && \
error "missing argument <name>\n$usage" && return 1
local sandbox=$SANDBOX_ROOT/$name
[[ ! -d $sandbox ]] && \
error "sandbox does not exist $name" && return 1
[[ "$PWD" = "$sandbox"* ]] && cd ~
rm -rf $sandbox
;;
list)
ls -1 $SANDBOX_ROOT | less -F -K -R -X
;;
enable)
local name=$1
[[ -z "$name" ]] && \
@@ -109,19 +118,16 @@ usage: sandbox [-h] {create,rename,destroy,enable,disable,list} ..
local sandbox=$SANDBOX_ROOT/$name
[[ ! -d $sandbox ]] && \
error "sandbox does not exist $name" && return 1
export SANDBOX_RETURN=$PWD
cd $sandbox
;;
disable)
[[ -z "$SANDBOX_RETURN" ]] && \
error "sandbox is not currently active" && return 1
cd $SANDBOX_RETURN
unset SANDBOX_RETURN
;;
*)
error "invalid sandbox command: $cmd" && return 1
;;
esac
}

View File

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

View File

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

83
tasks.yaml Normal file
View File

@@ -0,0 +1,83 @@
---
- name: zsh install packages
package:
name: zsh
state: present
- name: zsh install Debian packages
when: ansible_os_family == "Debian"
apt:
name: '{{item}}'
state: present
become: true
with_items:
- zsh-doc
- pinentry-curses
- unzip
- 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 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: which zsh
register: zsh
changed_when: false
- name: zsh set default shell
user:
name: '{{lookup("env", "USER")}}'
shell: '{{zsh.stdout}}'
become: true

19
update-completion-links.zsh Executable file
View File

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

27
url/url
View File

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

View File

@@ -26,86 +26,18 @@ fi
# Passthrough an escape sequences tmux doesn't know about.
tmux-dcs-passthrough() {
local escape_sequence=$1
if [ -n "$TMUX" ]; then
# Replace single \x1b or \033 with two, this is required for tmux to
# properly pass-through the escape sequence.
escape_sequence=${escape_sequence//\\x1b/\\x1b\\x1b}
escape_sequence=${escape_sequence//\\033/\\x1b\\x1b}
printf '\x1bPtmux;\x1b'"$escape_sequence"'\x1b\\'
printf "\x1bPtmux;\x1b$1\x1b\\"
else
printf "$escape_sequence"
printf "$1"
fi
}
# OSC 9 - Post a notification - supported by iTerm2, kitty, WezTerm, others?
# OSC 9 - Post a notification - supported by iTerm2, maybe others?
notify() {
tmux-dcs-passthrough '\x1b]9;'"$*"'\x7'
tmux-dcs-passthrough "\x1b]9;$*\x7"
}
# Send a desktop notification when long running commands complete.
notify_command_threshold=60
notify_ignore_list=(
bash
bat
btop
cat
cmatrix
fg
gh
git
glab
htop
ipython
man
nvim
ping
podman
python
session
slides
ssh
sudo
sudoedit
tmux
top
vi
vim
watch
zsh
)
notify-ignore() {
for ignore in $notify_ignore_list; do
if [[ "$1" = "$ignore"* ]]; then
return 0
fi
done
return 1
}
notify-preexec() {
if notify-ignore $1; then
return
fi
notify_command_start=`date +%s`
notify_command=$1
}
add-zsh-hook preexec notify-preexec
notify-precmd() {
if ! [[ -z $notify_command_start ]]; then
local notify_command_end=`date +%s`
local notify_command_time=$(($notify_command_end - $notify_command_start))
if [[ $notify_command_time -gt $notify_command_threshold ]]; then
notify "completed: $notify_command"
fi
unset notify_command
unset notify_command_start
fi
}
add-zsh-hook precmd notify-precmd
# Detect the type and extract an archive file.
extract() {
if [ -f $1 ]; then
@@ -134,11 +66,8 @@ if which bat &> /dev/null; then
# 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 "$@"
command bat --theme='TwoDark' --color always \
--paging always --pager 'less -R' "$@"
}
fi
@@ -170,26 +99,3 @@ if which docker-machine &> /dev/null; then
fi
}
fi
ls-iommu() {
$HOME/.config/zsh/ls-iommu.sh | sort -n
}
# Fuzzy history search with fzf
function .fzf-history-search() {
local selected
selected=$(
cat $HISTFILE | # get entire history
sed 's/ *[0-9]* *//' | # remove cruft
awk '!seen[$0]++' | # remove duplicates
fzf --layout=reverse --tac --cycle --info=hidden \
--border=rounded --height=50%
)
if [[ -n "$selected" ]]; then
BUFFER="$selected"
zle end-of-line
fi
zle reset-prompt
}
zle -N .fzf-history-search
bindkey '^R' .fzf-history-search

106
zshenv
View File

@@ -2,43 +2,30 @@
# contain commands that produce output or assume the shell is attached to a
# tty. This file will always be sourced.
[ -f ~/.config/zsh/zshenv.local ] && source ~/.config/zsh/zshenv.local
# Ensure cache and state directories exist
[ ! -d -${XDG_CACHE_HOME:-$HOME/.cache}/zsh ] && \
mkdir -p ${XDG_CACHE_HOME:-$HOME/.cache}/zsh
[ ! -d -${XDG_STATE_HOME:-$HOME/.local/state}/zsh ] && \
mkdir -p ${XDG_STATE_HOME:-$HOME/.local/state}/zsh
# Enable saving command history to file
HISTFILE=${XDG_STATE_HOME:-$HOME/.local/state}/zsh/histfile
[ ! -d $HOME/.cache/zsh ] && mkdir -p $HOME/.cache/zsh
HISTFILE=$HOME/.cache/zsh/histfile
HISTSIZE=20000
SAVEHIST=20000
# Migrate histfile from cache to state directory
! [ -f $HISTFILE ] && [ -f $HOME/.cache/zsh/histfile ] && \
mv $HOME/.cache/zsh/histfile \
${XDG_STATE_HOME:-$HOME/.local/state}/zsh/histfile
# Remove vi mode switch delay
KEYTIMEOUT=1
# Enable time stats for long lasting commands
REPORTTIME=5
# Add ~/.local to the environment
fpath+=$HOME/.local/share/zsh/site-functions
PATH=$HOME/.local/bin:$PATH
MANPATH=$HOME/.local/share/man:$MANPATH
INFOPATH=$HOME/.local/share/info:$INFOPATH
# Add ccache compiler aliases to PATH and use XDG base dir paths
if [ `uname` = Darwin ]; then
if [ `uname -m` = arm64 ]; then
homebrew_root=/opt/homebrew
[ -d /opt/homebrew/bin ] && \
PATH=$homebrew_root/bin:$PATH
else
homebrew_root=/usr/local
fi
[ -d $homebrew_root/opt/python/libexec/bin ] && \
PATH=$homebrew_root/opt/python/libexec/bin:$PATH
[ -f $homebrew_root/bin/ccache ] && \
PATH=$homebrew_root/opt/ccache/libexec:$PATH
[ -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
@@ -46,20 +33,12 @@ elif [ -f /usr/bin/ccache ]; then
PATH=/usr/lib/ccache:$PATH
fi
fi
export CCACHE_CONFIGPATH=${XDG_CONFIG_HOME:-$HOME/.config}/ccache
export CCACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/ccache
export CCACHE_CONFIGPATH=$HOME/.config/ccache
export CCACHE_DIR=$HOME/.cache/ccache
# Add ~/.local to the environment
fpath+=$HOME/.local/share/zsh/site-functions
PATH=$HOME/.local/bin:$PATH
MANPATH=$HOME/.local/share/man:$MANPATH
INFOPATH=$HOME/.local/share/info:$INFOPATH
# Add default CMake options
command -v ninja &> /dev/null && \
# Add default CMake generator
which ninja &> /dev/null && \
export CMAKE_GENERATOR=Ninja
export CMAKE_EXPORT_COMPILE_COMMANDS=ON
export CMAKE_COLOR_DIAGNOSTICS=ON
# Remove duplicates from environment variables
typeset -U fpath
@@ -68,22 +47,8 @@ typeset -U MANPATH; export MANPATH
typeset -U INFOPATH; export INFOPATH
# Set default editor.
if command -v nvim &> /dev/null; then
export EDITOR=`command -v nvim`
# Also use nvim for man pages
export MANPAGER='nvim +Man!'
elif command -v vim &> /dev/null; then
export EDITOR=`command -v vim`
fi
export GIT_EDITOR=$EDITOR
if command -v fzf &> /dev/null; then
export FZF_DEFAULT_OPTS='--no-bold
--color=fg:#c5c9c5,fg+:#c5c9c5,bg:#000000,bg+:#393836
--color=hl:#8ea4a2,hl+:#8ea4a2,info:#afaf87,marker:#C8C093
--color=prompt:#C8C093,spinner:#8992a7,pointer:#FF9E3B,header:#87afaf
--color=gutter:#000000,border:#54546D,label:#aeaeae,query:#c5c9c5'
fi
which vim &> /dev/null && \
export EDITOR=`which vim`
# Use ~/.local for pip installs on macOS
[ "`uname`" = "Darwin" ] && export PYTHONUSERBASE=$HOME/.local
@@ -112,33 +77,26 @@ export PYLINTHOME=~/.local/share/pylint
export VIRTUAL_ENV_DISABLE_PROMPT=1
# If pinentry-curses exists, use it for lastpass-cli
command -v pinentry-curses &> /dev/null && \
which pinentry-curses &> /dev/null && \
export LPASS_PINENTRY=pinentry-curses
# Teach these some XDG Base Directory Spec manners
export IPYTHONDIR=${XDG_CONFIG_HOME:-$HOME/.config}/ipython
command -v cargo &> /dev/null && \
export IPYTHONDIR=$HOME/.config/ipython
which cargo &> /dev/null && \
export CARGO_HOME=$HOME/.local/share/cargo
if command -v ccache &> /dev/null; then
export CCACHE_CONFIGPATH=${XDG_CONFIG_HOME:-$HOME/.config}/ccache.conf
export CCACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/ccache
if which ccache &> /dev/null; then
export CCACHE_CONFIGPATH=$HOME/.config/ccache.conf
export CCACHE_DIR=$HOME/.cache/ccache
fi
command -v conan &> /dev/null && \
which conan &> /dev/null && \
export CONAN_USER_HOME=$HOME/.local/share/conan
command -v docker &> /dev/null && \
which docker &> /dev/null && \
export DOCKER_CONFIG=$HOME/.local/share/docker
export GTK_RC_FILES=${XDG_CONFIG_HOME:-$HOME/.config}/gtk/gtkrc
export GTK2_RC_FILES=${XDG_CONFIG_HOME:-$HOME/.config}/gtk-2.0/gtkrc
export PYLINTHOME=${XDG_CACHE_HOME:-$HOME/.cache}/pylint
command -v rustup &> /dev/null && \
export 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
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/wget/rc ] && \
export WGETRC=${XDG_CONFIG_HOME:-$HOME/.config}/wget/rc
export PYLINTHOME=$HOME/.cache/pylint
# TODO: terminfo
export GOBIN=$HOME/.local/bin
export GOPATH=$HOME/.local/share/go
export GOCACHE=${XDG_CACHE_HOME:-$HOME/.cache}/go/build
export GOMODCACHE=${XDG_CACHE_HOME:-$HOME/.cache}/go/pkg/mod
export GOTMPDIR=${XDG_CACHE_HOME:-$HOME/.cache}/go/tmp
export PIXI_HOME=$HOME/.local
export PIXI_NO_PATH_UPDATE=1
[ -f $HOME/.config/wgetrc ] &&
export WGETRC=$HOME/.config/wgetrc

74
zshrc
View File

@@ -3,12 +3,8 @@
# Load plugin scripts
source-plugin() {
local shared_plugin=${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins/$1/$1.plugin.zsh
local local_plugin=${XDG_CONFIG_HOME:-$HOME/.config}/zsh/$1/$1.plugin.zsh
if [ -f $shared_plugin ]; then
source $shared_plugin
elif [ -f $local_plugin ]; then
source $local_plugin
if [ -d ~/.config/zsh/$1 ]; then
source ~/.config/zsh/$1/$1.plugin.zsh
else
echo "zsh plugin not found: $1"
fi
@@ -21,13 +17,9 @@ ZSH_AUTOSUGGEST_ACCEPT_WIDGETS=(end-of-line vi-end-of-line)
# Search history with a command substring
source-plugin zsh-history-substring-search
HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_FOUND=
HISTORY_SUBSTRING_SEARCH_HIGHLIGHT_NOT_FOUND=
# Command syntax highlighting
source-plugin zsh-syntax-highlighting
(( ${+ZSH_HIGHLIGHT_STYLES} )) || typeset -A ZSH_HIGHLIGHT_STYLES
ZSH_HIGHLIGHT_STYLES[precommand]=fg=green
# Build system helper commands
source-plugin build
@@ -41,9 +33,6 @@ source-plugin utilities
# Automatically source .enter and .exit scripts on cd
source-plugin autoenv
# Session manager
source-plugin session
# Layout tmux window commands
[ "$TMUX" != "" ] && source-plugin layout
@@ -76,10 +65,10 @@ setopt completeinword
# Initialize completions
autoload -U compinit
compinit -d ${XDG_CACHE_HOME:-$HOME/.cache}/zsh/compdump
compinit
# Add pip to the old completion engine if present
if command -v pip &> /dev/null; then
if which pip &> /dev/null; then
function _pip_completion {
local words cword
read -Ac words
@@ -129,6 +118,9 @@ bindkey -M vicmd 'j' history-substring-search-down
bindkey -r '^[h'
bindkey -M vicmd 'K' run-help
# Disable Ex mode with ':'
bindkey -rM vicmd ':'
# Enable '<Shirt><Tab>' reverse order completions
bindkey '^[[Z' reverse-menu-complete
@@ -178,39 +170,17 @@ if [[ ! -z "$cursor_block" && ! -z "$cursor_line" ]]; then
fi
# Frequntly used directories
function frequent-directory() {
if [ -d "$2" ]; then
# Replace - with _ in environment variable name.
local name=${1//-/_}
local value=$2
export $name=$value
hash -d $1
fi
}
frequent-directory Desktop "$HOME/Desktop"
frequent-directory Documents "$HOME/Documents"
frequent-directory Downloads "$HOME/Downloads"
frequent-directory Projects "$HOME/Projects"
frequent-directory Sandbox "$HOME/Sandbox"
frequent-directory cache "${XDG_CACHE_HOME:-$HOME/.cache}"
frequent-directory config "${XDG_CONFIG_HOME:-$HOME/.config}"
frequent-directory local "$HOME/.local"
function frequent-directory() { export $1; hash -d $1 }
frequent-directory Projects="$HOME/Projects"
# Load work related config
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/work/zshrc ] && \
source ${XDG_CONFIG_HOME:-$HOME/.config}/work/zshrc
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/private/zshrc ] && \
source ${XDG_CONFIG_HOME:-$HOME/.config}/private/zshrc
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/local ] && \
source ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/local
[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/zshrc.local ] && \
source ${XDG_CONFIG_HOME:-$HOME/.config}/zsh/zshrc.local
[ -f ~/.config/work/zshrc ] && source ~/.config/work/zshrc
# Aliases
alias grep='grep --color=always'
command -v cmake &> /dev/null && \
which cmake &> /dev/null && \
alias cninja='cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON'
command -v ssh &> /dev/null && \
which ssh &> /dev/null && \
alias ssh='TERM=xterm-256color ssh'
alias weather="curl wttr.in"
alias cls="clear && printf '\e[3J'"
@@ -218,29 +188,15 @@ alias cls="clear && printf '\e[3J'"
case `uname` in
Linux)
alias ls='ls -F --color=auto'
if command -v cgdb &> /dev/null; then
if which cgdb &> /dev/null; then
alias debug='cgdb --args'
elif command -v gdb &> /dev/null; then
elif which gdb &> /dev/null; then
alias debug='gdb --args'
fi
;;
Darwin)
alias ls='ls -GFh'
command -v lldb &> /dev/null && \
which lldb &> /dev/null && \
alias debug='lldb --'
;;
esac
command -v wol > /dev/null && \
alias wakeonlan='wol'
# Append any aliases to notify_ignore_list if they contain any ignore commands
# NOTE: Keep this at the end of ~/.zshrc so all aliases are processed
for name in ${(k)aliases}; do
cmd=${${${$(alias $name)#${name}=}#\'}#\"}
for ignore in $notify_ignore_list; do
if [[ "$cmd" = "$ignore"* ]]; then
notify_ignore_list+=( $name )
fi
done
done