diff --git a/shells/zsh/includes/auto-notify.zsh b/shells/zsh/includes/auto-notify.zsh index f5c09ad7..de635f76 100644 --- a/shells/zsh/includes/auto-notify.zsh +++ b/shells/zsh/includes/auto-notify.zsh @@ -2,5 +2,4 @@ source "$ZSH_FOLDER/plugins/zsh-auto-notify/auto-notify.plugin.zsh" export AUTO_NOTIFY_THRESHOLD=5 export AUTO_NOTIFY_TITLE="Hey! %command has just finished" export AUTO_NOTIFY_BODY="It completed in %elapsed seconds with exit code %exit_code" -AUTO_NOTIFY_IGNORE+=("bat" "zathura" "libreoffice" "lf") -disable_auto_notify +AUTO_NOTIFY_IGNORE+=("bat" "zathura" "libreoffice" "lf" "davmail" "neomutt" "newsboat" "w3m") diff --git a/shells/zsh/includes/completion.zsh b/shells/zsh/includes/completion.zsh index d32ea1d3..3aa91059 100644 --- a/shells/zsh/includes/completion.zsh +++ b/shells/zsh/includes/completion.zsh @@ -1,3 +1,5 @@ +fpath=("${ZSH_FOLDER}completion" $fpath) + # Make completion work autoload -U compinit zstyle ':completion:*' menu select @@ -19,30 +21,11 @@ compdef sshrc=ssh compdef v=vim compdef vi=vim -autoload bashcompinit -bashcompinit -_wp_complete() { - local OLD_IFS="$IFS" - local cur=${COMP_WORDS[COMP_CWORD]} - IFS=$'\n'; # want to preserve spaces at the end - local opts="$(wp cli completions --line="$COMP_LINE" --point="$COMP_POINT")" - if [[ "$opts" =~ \\s* ]] - then - COMPREPLY=( $(compgen -f -- $cur) ) - elif [[ $opts = "" ]] - then - COMPREPLY=( $(compgen -f -- $cur) ) - else - COMPREPLY=( ${opts[*]} ) - fi - IFS="$OLD_IFS" - return 0 -} -complete -o nospace -F _wp_complete wp +#autoload bashcompinit +#bashcompinit # Include hidden files in autocomplete: _comp_options+=(globdots) -fpath=("$ZSH_FOLDER/completion" $fpath) #source "$ZSH_FOLDER/plugins/fzf-tab/fzf-tab.zsh" diff --git a/shells/zsh/includes/fzf.zsh b/shells/zsh/includes/fzf.zsh index 41d56327..acc0d803 100644 --- a/shells/zsh/includes/fzf.zsh +++ b/shells/zsh/includes/fzf.zsh @@ -20,184 +20,155 @@ fi if [ "$sourced" = "True" ]; then - # CTRL-W to select a wordlist - __fsel_wordlist() { - local cmd="$FZF_DEFAULT_COMMAND --exclude \*.md --exclude \*.gif --exclude \*.jpg --exclude \*.png --exclude \*.lua --exclude \*.jar --exclude \*.pl '' /usr/share/wordlists/ | sed 's#^/usr/share/wordlists/##'" - setopt localoptions pipefail 2> /dev/null - eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS --preview 'bat --color=always {}'" $(__fzfcmd) -m "$@" | while read item; do - echo -n "${(q)item} " - done - local ret=$? - echo - return $ret + # jhComplete(){ + # local comp=$(echo $1 | cut -d':' -f1) + # local prompt=$(echo $1 | cut -d':' -f2) + # local currentProject=$(project current --path) + # find "$HOME/.local/share/snippets/" -name \*.func | while read line; do + # source "$line" + # done + # case "${comp}" in + # ip) + # if [ -n "$currentProject" ]; then + # project hosts ip --fzf + # fi + # ;; + # myip) + # ip route | grep -oE '(dev|src) [^ ]+' | sed 'N;s/\n/,/;s/src //' | awk -F',' '{print $2 " " $1}' | sort -u | fzf --no-preview | cut -d' ' -f1 + # ;; + # pf) + # if [ -n "$currentProject" ]; then + # find "$currentProject" -type f | fzf + # fi + # ;; + # pd) + # if [ -n "$currentProject" ]; then + # find "$currentProject" -type d | fzf --no-preview + # fi + # ;; + # wl|wordlist) + # __fsel_wordlist + # ;; + # snip) + # snippets + # ;; + # network-interface|int) + # ip -o link show | cut -d' ' -f2- | sed -E 's/[^:]+(UP|DOWN).*/\1/' | tr ':' ' ' | fzf --preview="interface=\$(echo {} | cut -f1 -d' ');ip address show \$interface" | cut -d' ' -f1 + # ;; + # *) + # if command -v "jhcomplete::$comp" > /dev/null; then + # "jhcomplete::$comp" + # else + # echo "" + # fi + # esac + # } + + + # on_word_replace(){ + # setopt localoptions noshwordsplit noksh_arrays noposixbuiltins + # local word="${LBUFFER##* }${RBUFFER%% *}" + # if [ -n "$word" ]; then + # local changeto=$(jhswap "$word" ) + # local lastWord="$changeto" + # local LWORDS=$(echo $LBUFFER | tr ' ' '\n' | wc -l) + # local RWORDS=$(echo $RBUFFER | tr ' ' '\n' | wc -l) + # if [ "$LWORDS" -gt "1" ]; then + # LBUFFER="${LBUFFER% *} $lastWord" + # else + # LBUFFER="$lastWord" + # fi + # if [ "$RWORDS" -gt "1" ]; then + # RBUFFER=" ${RBUFFER#* }" + # else + # RBUFFER="" + # fi + # zle reset-prompt + # zle -R + # return 0 + # fi + + # } + + # Prompts the user to select a wordlist from the wordlists folder + wordlistSelect() { + fd -a --type f --hidden --follow --color=always --exclude .git --exclude \*.md --exclude \*.gif --exclude \*.jpg --exclude \*.png --exclude \*.lua --exclude \*.jar --exclude \*.pl '' /usr/share/wordlists/ | fzf --preview 'bat --color=always {}' } - fzf-wordlist-widget() { - LBUFFER="${LBUFFER}$(__fsel_wordlist)" - local ret=$? - zle reset-prompt - return $ret - } - #zle -N fzf-wordlist-widget - #bindkey '^W' fzf-wordlist-widget - - # CTRL-P to select an IP address from project host - __fsel_ip() { - setopt localoptions pipefail 2> /dev/null - project hosts ip --fzf - - local ret=$? - return $ret - } - - fzf-ip-widget() { - LBUFFER="${LBUFFER}$(project hosts ip --fzf) " - local ret=$? - zle reset-prompt - return $ret - } - #zle -N fzf-ip-widget - #bindkey '^P' fzf-ip-widget - - - jhComplete(){ - local comp=$(echo $1 | cut -d':' -f1) - local prompt=$(echo $1 | cut -d':' -f2) - local currentProject=$(project current --path) - find "$HOME/.local/share/snippets/" -name \*.func | while read line; do - source "$line" - done - case "${comp}" in - ip) - if [ -n "$currentProject" ]; then - project hosts ip --fzf - fi - ;; - myip) - ip route | grep -oE '(dev|src) [^ ]+' | sed 'N;s/\n/,/;s/src //' | awk -F',' '{print $2 " " $1}' | sort -u | fzf --no-preview | cut -d' ' -f1 - ;; - pf) - if [ -n "$currentProject" ]; then - find "$currentProject" -type f | fzf - fi - ;; - pd) - if [ -n "$currentProject" ]; then - find "$currentProject" -type d | fzf --no-preview - fi - ;; - wl|wordlist) - __fsel_wordlist - ;; - snip) - snippets - ;; - network-interface|int) - ip -o link show | cut -d' ' -f2- | sed -E 's/[^:]+(UP|DOWN).*/\1/' | tr ':' ' ' | fzf --preview="interface=\$(echo {} | cut -f1 -d' ');ip address show \$interface" | cut -d' ' -f1 - ;; - *) - if command -v "jhcomplete::$comp" > /dev/null; then - "jhcomplete::$comp" - else - echo "" - fi + word_replace(){ + local ret=1 + local word="$1" + local cmd="$2" + case "$word" in + wl) wordlistSelect; return 0 ;; + myip) ip route | grep -oE '(dev|src) [^ ]+' | sed 'N;s/\n/,/;s/src //;s/dev //' | awk -F',' '{print $2 " " $1}' | sort -u | fzf -1 --no-preview | cut -d' ' -f1; return 0 ;; esac + return "$ret" } -jhswap(){ - local orig="$1" - local inside=$(echo "$orig" | grep -Eo '<[^>]+>' | tr -d '<>') - local new=$(jhComplete "$inside") - echo "${orig/$inside/$new}" | tr -d '<>' - } - - on_word_replace(){ - setopt localoptions noshwordsplit noksh_arrays noposixbuiltins - local word="${LBUFFER##* }${RBUFFER%% *}" - if [ -n "$word" ]; then - local changeto=$(jhswap "$word" ) - local lastWord="$changeto" - local LWORDS=$(echo $LBUFFER | tr ' ' '\n' | wc -l) - local RWORDS=$(echo $RBUFFER | tr ' ' '\n' | wc -l) - if [ "$LWORDS" -gt "1" ]; then - LBUFFER="${LBUFFER% *} $lastWord" - else - LBUFFER="$lastWord" - fi - if [ "$RWORDS" -gt "1" ]; then - RBUFFER=" ${RBUFFER#* }" - else - RBUFFER="" - fi - zle reset-prompt - zle -R - return 0 - fi - - } - - # I want my tab complete to be based on "current" word I am typing sometimes, before the command - custom_tabcomplete(){ - local tokens cmd prefix trigger tail fzf matches lbuf d_cmds - FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS --reverse --height 40%" - setopt localoptions noshwordsplit noksh_arrays noposixbuiltins - - if [ -n "$RBUFFER" ]; then - if [[ "${RBUFFER[1]}" != " " ]]; then - on_word_replace - return 0 - fi - fi - - + jh_tabcomplete(){ + local tokens cmd swap ret=1 lastWord # http://zsh.sourceforge.net/FAQ/zshfaq03.html # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags + + # Use zsh's shell parsing to split lbuffer into items + # This takes into account quoting and escaping tokens=(${(z)LBUFFER}) + + # If there isn't anythng typed, fall back to old tab binding if [ ${#tokens} -lt 1 ]; then - zle ${fzf_default_completion:-expand-or-complete} + zle ${jhcomplete_default_completion:-expand-or-complete} return fi - cmd=${tokens[1]} - append="" - if [[ "$cmd" == "hashcat" ]]; then - if [[ "${tokens[-1]}" == "-m" || "${tokens[-1]}" == "--hash-type" ]]; then - append=$(hashcat --example-hashes | awk -v RS="\n\n" -F "\t" '{gsub("\n","\t",$0); print $1 "\t" $2 "\t" $3}' | sed 's/MODE: //; s/TYPE: //' | fzf -d "\t" --header="Mode Type" --with-nth='1,2' --preview='echo {3}' --preview-window=up:1 | cut -d' ' -f1) - #else - # append=$(hashcat --help | sed -n '/Options Short/,/^$/p' | sed -E 's/ +/ /g' | sed '/==============/d' | column -t -s '|' -o " " | fzf -d " " --with-nth="1,3" --tabstop=2 --header-lines="1" --no-preview | cut -d' ' -f1 | sed 's/\s*$//' | grep -Eo '\S+$') + + # Assume the first element + # TODO: make this work for multiple commands chained with | or > or && etc. + # TODO: make this work when command prepended with variables eg a=2 foo bar + # foo is the command name + cmd="${tokens[1]}" + lastWord="${tokens[-1]}" + + # This part checks to see if the whole "word" is replaceable + if [ "${LBUFFER[-1]}" != " " ]; then + swap="$(word_replace "$lastWord" "$cmd")" + ret="$?" + fi + + # This part checks if the part after an = is completable + if [ "$ret" -ne "0" ]; then + local afterEqual="${lastWord##*=}" + local beforeEqual="${lastWord%=*}" + # If they are different, there is an equals in the word + if [ "$afterEqual" != "$lastWord" ]; then + swap="${beforeEqual}=$(word_replace "$afterEqual" "$cmd")" + ret="$?" fi fi - if [ -n "$append" ]; then - # Make sure that we are adding a space - if [[ "${LBUFFER[-1]}" != " " ]]; then - LBUFFER="${LBUFFER} " + + if [ "$ret" -eq "0" ]; then + if [ -n "$swap" ]; then + tokens[-1]="$swap" + LBUFFER="${tokens[@]}" fi - LBUFFER="${LBUFFER}${append}" zle reset-prompt - return $ret return 0 + else + zle ${jhcomplete_default_completion:-expand-or-complete} + # Redrawing here means I can use fzf in my completion functions + # This is not ideal, I would preferably run this after fzf in the completion functions but can't because they are not widgets. + zle reset-prompt + return fi - #local newLBuffer="${tokens:1:${#tokens[@]}-1}" - local newLBuffer - for i in $(seq 1 $((${#tokens[@]} - 1)) ); do - newLBuffer="${newLBuffer}${tokens[i]} " - done - - - if [[ "${LBUFFER[-1]}" == " " ]]; then - fzf-completion - else - local new=$(jhComplete "${tokens[-1]}") - if [ -n "$new" ]; then - LBUFFER="${newLBuffer}${new} " - local ret=$? - zle reset-prompt - return $ret - else - fzf-completion - fi - fi } - zle -N custom_tabcomplete - bindkey '^I' custom_tabcomplete + + # Record what ctrl+i is currently set to + # That way we can call it if jhcompletion doesn't result in anything + [ -z "$jhcomplete_default_completion" ] && { + binding=$(bindkey '^I') + [[ $binding =~ 'undefined-key' ]] || jhcomplete_default_completion=$binding[(s: :w)2] + unset binding + } + zle -N jh_tabcomplete + bindkey '^I' jh_tabcomplete fi diff --git a/shells/zsh/includes/keybindings.zsh b/shells/zsh/includes/keybindings.zsh index 8b87ba89..bfe19799 100644 --- a/shells/zsh/includes/keybindings.zsh +++ b/shells/zsh/includes/keybindings.zsh @@ -79,3 +79,13 @@ function fg-bg() { } zle -N fg-bg bindkey '^Z' fg-bg + +rationalise-dot() { + if [[ $LBUFFER = *.. ]]; then + LBUFFER+=/.. + else + LBUFFER+=. + fi +} +zle -N rationalise-dot +bindkey . rationalise-dot