diff --git a/dot_zprofile b/dot_zprofile new file mode 100644 index 0000000..4121e30 --- /dev/null +++ b/dot_zprofile @@ -0,0 +1,26 @@ +#!/usr/bin/env zsh +export PATH=$PATH:/sbin/ +export PATH=$PATH:$HOME/.emacs.d/bin +export PATH=$PATH:$HOME/bin +export PATH=$PATH:$HOME/.config/emacs/bin +export PATH=$PATH:$HOME/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/ + + + +export XDG_DATA_DIRS=$XDG_DATA_DIRS:$HOME +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +export PATH=$HOME/.local/share/ponyup/bin:$PATH + +export PATH=$PATH:$HOME.millennium/ext/bin +export PATH="$PATH:$HOME/Code/flutter/bin" +export PATH="$PATH:$HOME/Android/Sdk/cmdline-tools/bin" +export SSH_AUTH_SOCK=/home/$USER/.bitwarden-ssh-agent.sock + +# pnpm +export PNPM_HOME="/home/linly/.local/share/pnpm" +case ":$PATH:" in + *":$PNPM_HOME:"*) ;; + *) export PATH="$PNPM_HOME:$PATH" ;; +esac +# pnpm end + diff --git a/dot_zsh-vi-mode/zsh-vi-mode.zsh b/dot_zsh-vi-mode/zsh-vi-mode.zsh index 9d86d6f..c3d40f1 100644 --- a/dot_zsh-vi-mode/zsh-vi-mode.zsh +++ b/dot_zsh-vi-mode/zsh-vi-mode.zsh @@ -130,7 +130,7 @@ # ZVM_LINE_INIT_MODE # the setting for init mode of command line (default is empty), empty will # keep the last command mode, for the first command line it will be insert -# mode, you can also set it to a specific vi mode to alway keep the mode +# mode, you can also set it to a specific vi mode to always keep the mode # for each command line # # For example: @@ -253,7 +253,7 @@ ZVM_MODE='' # The keys typed to invoke this widget, as a literal string ZVM_KEYS='' -# The region hilight information +# The region highlight information ZVM_REGION_HIGHLIGHT=() # Default zvm readkey engines @@ -324,7 +324,7 @@ if $ZVM_LAZY_KEYBINDINGS; then ZVM_LAZY_KEYBINDINGS_LIST=() fi -# Set the cursor style in defferent vi modes, the value you could use +# Set the cursor style in different vi modes, the value you could use # the predefined value, such as $ZVM_CURSOR_BLOCK, $ZVM_CURSOR_BEAM, # $ZVM_CURSOR_BLINKING_BLOCK and so on. : ${ZVM_INSERT_MODE_CURSOR:=$ZVM_CURSOR_BEAM} @@ -662,7 +662,7 @@ function zvm_bindkey() { return fi - # Hanle the keybinding of NEX readkey engine + # Handle the keybinding of NEX readkey engine if [[ $ZVM_READKEY_ENGINE == $ZVM_READKEY_ENGINE_NEX ]]; then # Get the first key (especially check if ctrl characters) if [[ $#keys -gt 1 && "${keys:0:1}" == '^' ]]; then @@ -713,4 +713,3327 @@ function zvm_escape_non_printed_characters() { str="${str}^${c}" elif [[ "$c" == '' ]]; then str="${str}^?" - elif [[ "$c" == ' + elif [[ "$c" == '' ]]; then + str="${str}^@" + else + str="${str}${c}" + fi + done + + # Escape the newline and space characters, otherwise, we can't + # get the output from subshell correctly. + str=${str// /$ZVM_ESCAPE_SPACE} + str=${str//$'\n'/$ZVM_ESCAPE_NEWLINE} + + echo -n $str +} + +# Backward remove characters of an emacs region in the line +function zvm_backward_kill_region() { + local bpos=$CURSOR-1 epos=$CURSOR + + # Backward search the boundary of current region + for ((; bpos >= 0; bpos--)); do + # Break when cursor is at the beginning of line + [[ "${BUFFER:$bpos:1}" == $'\n' ]] && break + + # Break when cursor is at the boundary of a word region + [[ "${BUFFER:$bpos:2}" =~ ^\ [^\ $'\n']$ ]] && break + done + + bpos=$bpos+1 + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$bpos + zvm_clipboard_copy_buffer +} + +# Remove all characters between the cursor position and the +# beginning of the line. +function zvm_backward_kill_line() { + BUFFER=${BUFFER:$CURSOR:$#BUFFER} + CURSOR=0 +} + +# Remove all characters between the cursor position and the +# end of the line. +function zvm_forward_kill_line() { + BUFFER=${BUFFER:0:$CURSOR} +} + +# Remove all characters of the line. +function zvm_kill_line() { + local ret=($(zvm_calc_selection $ZVM_MODE_VISUAL_LINE)) + local bpos=${ret[1]} epos=${ret[2]} + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))}$'\n' + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$bpos + zvm_clipboard_copy_buffer +} + +# Remove all characters of the whole line. +function zvm_kill_whole_line() { + local ret=($(zvm_calc_selection $ZVM_MODE_VISUAL_LINE)) + local bpos=$ret[1] epos=$ret[2] cpos=$ret[3] + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))}$'\n' + + # Adjust region range of deletion + if (( $epos < $#BUFFER )); then + epos=$epos+1 + fi + + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$cpos + zvm_clipboard_copy_buffer +} + +# Exchange the point and mark +function zvm_exchange_point_and_mark() { + cursor=$MARK + MARK=$CURSOR CURSOR=$cursor + zvm_highlight +} + +# Open line below +function zvm_open_line_below() { + local i=$CURSOR + + # If there is a completion suffix, we should break at the + # position of suffix begin, otherwise, it should break when + # forward finding out the first newline character. + for ((; i<$#BUFFER; i++)); do + if ((SUFFIX_ACTIVE == 1)) && ((i >= SUFFIX_BEGIN)); then + break + fi + if [[ "${BUFFER[$i]}" == $'\n' ]]; then + i=$((i-1)) + break + fi + done + + CURSOR=$i + LBUFFER+=$'\n' + + zvm_reset_repeat_commands $ZVM_MODE_NORMAL o + zvm_select_vi_mode $ZVM_MODE_INSERT +} + +# Open line above +function zvm_open_line_above() { + local i=$CURSOR + + # Break when backward finding out the first newline character. + for ((; i>0; i--)); do + if [[ "${BUFFER[$i]}" == $'\n' ]]; then + break + fi + done + + CURSOR=$i + LBUFFER+=$'\n' + CURSOR=$((CURSOR-1)) + + zvm_reset_repeat_commands $ZVM_MODE_NORMAL O + zvm_select_vi_mode $ZVM_MODE_INSERT +} + +# Replace characters one by one (Replacing mode) +function zvm_vi_replace() { + if [[ $ZVM_MODE == $ZVM_MODE_NORMAL ]]; then + local cursor=$CURSOR + local cache=() + local cmds=() + local key= + + zvm_select_vi_mode $ZVM_MODE_REPLACE + + while :; do + # Read a character for replacing + zvm_update_cursor + + # Redisplay the command line, this is to be called from within + # a user-defined widget to allow changes to become visible + zle -R + + read -k 1 key + + # Escape key will break the replacing process, and enter key + # will replace with a newline character. + case $(zvm_escape_non_printed_characters $key) in + '^['|$ZVM_VI_OPPEND_ESCAPE_BINDKEY) break;; + '^M') key=$'\n';; + esac + + # If the key is backspace, we should move backward the cursor + if [[ $key == '' ]]; then + # Cursor position should not be less than zero + if ((cursor > 0)); then + cursor=$((cursor-1)) + fi + + # We should recover the character when cache size is not zero + if ((${#cache[@]} > 0)); then + key=${cache[-1]} + + if [[ $key == '' ]]; then + key= + fi + + cache=(${cache[@]:0:-1}) + BUFFER[$cursor+1]=$key + + # Remove from commands + cmds=(${cmds[@]:0:-1}) + fi + else + # If the key or the character at cursor is a newline character, + # or the cursor is at the end of buffer, we should insert the + # key instead of replacing with the key. + if [[ $key == $'\n' || + $BUFFER[$cursor+1] == $'\n' || + $BUFFER[$cursor+1] == '' + ]]; then + cache+=('') + LBUFFER+=$key + else + cache+=(${BUFFER[$cursor+1]}) + BUFFER[$cursor+1]=$key + fi + + cursor=$((cursor+1)) + + # Push to commands + cmds+=($key) + fi + + # Update next cursor position + CURSOR=$cursor + + zle redisplay + done + + # The cursor position should go back one character after + # exiting the replace mode + zle vi-backward-char + + zvm_select_vi_mode $ZVM_MODE_NORMAL + zvm_reset_repeat_commands $ZVM_MODE R $cmds + elif [[ $ZVM_MODE == $ZVM_MODE_VISUAL ]]; then + zvm_enter_visual_mode V + zvm_vi_change + elif [[ $ZVM_MODE == $ZVM_MODE_VISUAL_LINE ]]; then + zvm_vi_change + fi +} + +# Replace characters in one time +function zvm_vi_replace_chars() { + local cmds=() + local key= + + # Read a character for replacing + zvm_enter_oppend_mode + + # Redisplay the command line, this is to be called from within + # a user-defined widget to allow changes to become visible + zle redisplay + zle -R + + read -k 1 key + + zvm_exit_oppend_mode + + # Escape key will break the replacing process + case $(zvm_escape_non_printed_characters $key) in + $ZVM_VI_OPPEND_ESCAPE_BINDKEY) + zvm_exit_visual_mode + return + esac + + if [[ $ZVM_MODE == $ZVM_MODE_NORMAL ]]; then + cmds+=($key) + BUFFER[$CURSOR+1]=$key + else + local ret=($(zvm_calc_selection)) + local bpos=${ret[1]} epos=${ret[2]} + for ((bpos=bpos+1; bpos<=epos; bpos++)); do + # Newline character is no need to be replaced + if [[ $BUFFER[$bpos] == $'\n' ]]; then + cmds+=($'\n') + continue + fi + + cmds+=($key) + BUFFER[$bpos]=$key + done + zvm_exit_visual_mode + fi + + # Reset the repeat commands + zvm_reset_repeat_commands $ZVM_MODE r $cmds +} + +# Substitute characters of selection +function zvm_vi_substitute() { + # Substitute one character in normal mode + if [[ $ZVM_MODE == $ZVM_MODE_NORMAL ]]; then + BUFFER="${BUFFER:0:$CURSOR}${BUFFER:$((CURSOR+1))}" + zvm_reset_repeat_commands $ZVM_MODE c 0 1 + zvm_select_vi_mode $ZVM_MODE_INSERT + else + zvm_vi_change + fi +} + +# Substitute all characters of a line +function zvm_vi_substitute_whole_line() { + zvm_select_vi_mode $ZVM_MODE_VISUAL_LINE false + zvm_vi_substitute +} + +# Check if cursor is at an empty line +function zvm_is_empty_line() { + local cursor=${1:-$CURSOR} + if [[ ${BUFFER:$cursor:1} == $'\n' && + ${BUFFER:$((cursor-1)):1} == $'\n' ]]; then + return + fi + return 1 +} + +# Get the beginning and end position of selection +function zvm_selection() { + local bpos= epos= + if (( MARK > CURSOR )) ; then + bpos=$CURSOR epos=$((MARK+1)) + else + bpos=$MARK epos=$((CURSOR+1)) + fi + echo $bpos $epos +} + +# Calculate the region of selection +function zvm_calc_selection() { + local ret=($(zvm_selection)) + local bpos=${ret[1]} epos=${ret[2]} cpos= + + # Save the current cursor position + cpos=$bpos + + # Check if it is visual-line mode + if [[ "${1:-$ZVM_MODE}" == $ZVM_MODE_VISUAL_LINE ]]; then + + # Extend the selection to whole line + for ((bpos=$bpos-1; $bpos>0; bpos--)); do + if [[ "${BUFFER:$bpos:1}" == $'\n' ]]; then + bpos=$((bpos+1)) + break + fi + done + for ((epos=$epos-1; $epos<$#BUFFER; epos++)); do + if [[ "${BUFFER:$epos:1}" == $'\n' ]]; then + break + fi + done + + # The begin position must not be less than zero + if (( bpos < 0 )); then + bpos=0 + fi + + ########################################### + # Calculate the new cursor position, here we consider that + # the selection will be delected. + + # Calculate the indent of current cursor line + for ((cpos=$((CURSOR-1)); $cpos>=0; cpos--)); do + [[ "${BUFFER:$cpos:1}" == $'\n' ]] && break + done + + local indent=$((CURSOR-cpos-1)) + + # If the selection includes the last line, the cursor + # will move up to above line. Otherwise the cursor will + # keep in the same line. + + local hpos= # Line head position + local rpos= # Reference position + + if (( $epos < $#BUFFER )); then + # Get the head position of next line + hpos=$((epos+1)) + rpos=$bpos + else + # Get the head position of above line + for ((hpos=$((bpos-2)); $hpos>0; hpos--)); do + if [[ "${BUFFER:$hpos:1}" == $'\n' ]]; then + break + fi + done + if (( $hpos < -1 )); then + hpos=-1 + fi + hpos=$((hpos+1)) + rpos=$hpos + fi + + # Calculate the cursor position, the indent must be + # less than the line characters. + for ((cpos=$hpos; $cpos<$#BUFFER; cpos++)); do + if [[ "${BUFFER:$cpos:1}" == $'\n' ]]; then + break + fi + if (( $hpos + $indent <= $cpos )); then + break + fi + done + + cpos=$((rpos+cpos-hpos)) + fi + + echo $bpos $epos $cpos +} + +# Yank characters of the marked region +function zvm_yank() { + local ret=($(zvm_calc_selection $1)) + local bpos=$ret[1] epos=$ret[2] cpos=$ret[3] + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + if [[ ${1:-$ZVM_MODE} == $ZVM_MODE_VISUAL_LINE ]]; then + CUTBUFFER=${CUTBUFFER}$'\n' + fi + CURSOR=$bpos MARK=$epos + zvm_clipboard_copy_buffer +} + +# Up case of the visual selection +function zvm_vi_up_case() { + local ret=($(zvm_selection)) + local bpos=${ret[1]} epos=${ret[2]} + local content=${BUFFER:$bpos:$((epos-bpos))} + BUFFER="${BUFFER:0:$bpos}${(U)content}${BUFFER:$epos}" + zvm_exit_visual_mode +} + +# Down case of the visual selection +function zvm_vi_down_case() { + local ret=($(zvm_selection)) + local bpos=${ret[1]} epos=${ret[2]} + local content=${BUFFER:$bpos:$((epos-bpos))} + BUFFER="${BUFFER:0:$bpos}${(L)content}${BUFFER:$epos}" + zvm_exit_visual_mode +} + +# Opposite case of the visual selection +function zvm_vi_opp_case() { + local ret=($(zvm_selection)) + local bpos=${ret[1]} epos=${ret[2]} + local content=${BUFFER:$bpos:$((epos-bpos))} + local i= + for ((i=1; i<=$#content; i++)); do + if [[ ${content[i]} =~ [A-Z] ]]; then + content[i]=${(L)content[i]} + elif [[ ${content[i]} =~ [a-z] ]]; then + content[i]=${(U)content[i]} + fi + done + BUFFER="${BUFFER:0:$bpos}${content}${BUFFER:$epos}" + zvm_exit_visual_mode +} + +# Yank characters of the visual selection +function zvm_vi_yank() { + zvm_yank + zvm_exit_visual_mode ${1:-true} +} + +# Put cutbuffer after the cursor +function zvm_vi_put_after() { + local count=${NUMERIC:-1} + local head= foot= + local content=${CUTBUFFER} + local offset=1 + + if [[ ${content: -1} == $'\n' ]]; then + local pos=${CURSOR} + + # Find the end of current line + for ((; $pos<$#BUFFER; pos++)); do + if [[ ${BUFFER:$pos:1} == $'\n' ]]; then + pos=$pos+1 + break + fi + done + + head=${BUFFER:0:$pos} + foot=${BUFFER:$pos} + + # If at end of buffer (no trailing newline), prepend one and drop trailing one + if ! zvm_is_empty_line; then + if [[ $pos == $#BUFFER ]]; then + content=$'\n'${content:0:-1} + pos=$pos+1 + head=${BUFFER:0:$pos} + foot=${BUFFER:$pos} + fi + fi + + local repeated= i= + for ((i=1; i<=count; i++)); do + repeated+="$content" + done + + offset=0 + BUFFER="${head}${repeated}${foot}" + CURSOR=$pos + else + local char_at_cursor=${BUFFER:$CURSOR:1} + + # Special handling if cursor at an empty line + if zvm_is_empty_line; then + head="${BUFFER:0:$((CURSOR-1))}" + foot="${BUFFER:$CURSOR}" + else + head="${BUFFER:0:$CURSOR}" + foot="${BUFFER:$((CURSOR+1))}" + fi + + local repeated= i= + for ((i=1; i<=count; i++)); do + repeated+="$content" + done + + BUFFER="${head}${char_at_cursor}${repeated}${foot}" + CURSOR=$CURSOR+$#repeated + fi + + # Refresh display and highlight buffer + zvm_highlight clear + zvm_highlight custom $(($#head+$offset)) $(($#head+$#repeated+$offset)) +} + +# Put cutbuffer before the cursor +function zvm_vi_put_before() { + local count=${NUMERIC:-1} + local head= foot= + local content=${CUTBUFFER} + + if [[ ${content: -1} == $'\n' ]]; then + local pos=$CURSOR + + # Find the beginning of current line + for ((; $pos>0; pos--)); do + if [[ "${BUFFER:$pos:1}" == $'\n' ]]; then + pos=$pos+1 + break + fi + done + + # Check if it is an empty line + if zvm_is_empty_line; then + head=${BUFFER:0:$((pos-1))} + foot=$'\n'${BUFFER:$pos} + pos=$((pos-1)) + else + head=${BUFFER:0:$pos} + foot=${BUFFER:$pos} + fi + + local repeated= i= + for ((i=1; i<=count; i++)); do + repeated+="$content" + done + + BUFFER="${head}${repeated}${foot}" + CURSOR=$pos + else + head="${BUFFER:0:$CURSOR}" + foot="${BUFFER:$((CURSOR+1))}" + + local repeated= i= + for ((i=1; i<=count; i++)); do + repeated+="$content" + done + + BUFFER="${head}${repeated}${BUFFER:$CURSOR:1}${foot}" + CURSOR=$CURSOR+$#repeated + CURSOR=$((CURSOR-1)) + fi + + # Refresh display and highlight buffer + zvm_highlight clear + zvm_highlight custom $#head $(($#head+$#repeated)) +} + +# Replace a selection +function zvm_replace_selection() { + local ret=($(zvm_calc_selection)) + local bpos=$ret[1] epos=$ret[2] cpos=$ret[3] + local cutbuf=$1 + + # If there's a replacement, we need to calculate cursor position + if (( $#cutbuf > 0 )); then + cpos=$(($bpos + $#cutbuf - 1)) + fi + + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + + # Check if it is visual line mode + if [[ $ZVM_MODE == $ZVM_MODE_VISUAL_LINE ]]; then + if (( $epos < $#BUFFER )); then + epos=$epos+1 + elif (( $bpos > 0 )); then + bpos=$bpos-1 + fi + CUTBUFFER=${CUTBUFFER}$'\n' + fi + + BUFFER="${BUFFER:0:$bpos}${cutbuf}${BUFFER:$epos}" + CURSOR=$cpos + zvm_clipboard_copy_buffer +} + +# Replace characters of the visual selection +function zvm_vi_replace_selection() { + zvm_replace_selection $CUTBUFFER + zvm_exit_visual_mode ${1:-true} +} + +# Delete characters of the visual selection +function zvm_vi_delete() { + zvm_replace_selection + zvm_exit_visual_mode ${1:-true} +} + +# Yank characters of the visual selection +function zvm_vi_change() { + local ret=($(zvm_calc_selection)) + local bpos=$ret[1] epos=$ret[2] + + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + + # Check if it is visual line mode + if [[ $ZVM_MODE == $ZVM_MODE_VISUAL_LINE ]]; then + CUTBUFFER=${CUTBUFFER}$'\n' + fi + + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$bpos + zvm_clipboard_copy_buffer + + # Return when it's repeating mode + $ZVM_REPEAT_MODE && return + + # Reset the repeat commands + if [[ $ZVM_MODE != $ZVM_MODE_NORMAL ]]; then + local npos=0 ncount=0 ccount=0 + # Count the amount of newline character and the amount of + # characters after the last newline character. + while :; do + # Forward find the last newline character's position + npos=$(zvm_substr_pos $CUTBUFFER $'\n' $npos) + if [[ $npos == -1 ]]; then + if (($ncount == 0)); then + ccount=$#CUTBUFFER + fi + break + fi + npos=$((npos+1)) + ncount=$(($ncount + 1)) + ccount=$(($#CUTBUFFER - $npos)) + done + zvm_reset_repeat_commands $ZVM_MODE c $ncount $ccount + fi + + zvm_exit_visual_mode false + zvm_select_vi_mode $ZVM_MODE_INSERT ${1:-true} +} + +# Change characters from cursor to the end of current line +function zvm_vi_change_eol() { + local bpos=$CURSOR epos=$CURSOR + + # Find the end of current line + for ((; $epos<$#BUFFER; epos++)); do + if [[ "${BUFFER:$epos:1}" == $'\n' ]]; then + break + fi + done + + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + + zvm_clipboard_copy_buffer + zvm_reset_repeat_commands $ZVM_MODE c 0 $#CUTBUFFER + zvm_select_vi_mode $ZVM_MODE_INSERT +} + +# Default handler for unhandled key events +function zvm_default_handler() { + local keys=$(zvm_keys) + local extra_keys=$1 + + # Exit vi mode if keys is the escape keys + case $(zvm_escape_non_printed_characters "$keys") in + '^['|$ZVM_VI_INSERT_ESCAPE_BINDKEY) + zvm_exit_insert_mode false + zvm_reset_prompt + ZVM_KEYS=${extra_keys} + return + ;; + [vV]'^['|[vV]$ZVM_VI_VISUAL_ESCAPE_BINDKEY) + zvm_exit_visual_mode false + zvm_reset_prompt + ZVM_KEYS=${extra_keys} + return + ;; + esac + + case "$KEYMAP" in + vicmd) + case "$keys" in + [vV]c) zvm_vi_change false;; + [vV]d) zvm_vi_delete false;; + [vV]y) zvm_vi_yank false;; + [vV]S) zvm_change_surround S;; + [cdyvV]*) + # We must loop util we meet a valid range action + while :; do + zvm_range_handler "${keys}${extra_keys}" + case $? in + $ZVM_RANGE_HANDLER_RET_OK) + # The range action is handled successfully and exit + break + ;; + $ZVM_RANGE_HANDLER_RET_CONTINUE) + # Continue to ask to provide the action when we're + # still in visual mode + keys='v'; extra_keys= + ;; + $ZVM_RANGE_HANDLER_RET_PUSHBACK) + # Push the keys onto the input stack of ZLE, it's + # handled in zvm_readkeys_handler function + zvm_exit_visual_mode false + zvm_reset_prompt + return + ;; + $ZVM_RANGE_HANDLER_RET_CANCEL) + # Exit visual mode and cancel the range action + zvm_exit_visual_mode false + zvm_reset_prompt + break + ;; + esac + done + ;; + *) + local i= + for ((i=0;i<$#keys;i++)) do + zvm_navigation_handler ${keys:$i:1} + zvm_highlight + done + ;; + esac + ;; + viins|main) + if [[ "${keys:0:1}" =~ [a-zA-Z0-9\ ] ]]; then + zvm_self_insert "${keys:0:1}" + zle redisplay + ZVM_KEYS="${keys:1}${extra_keys}" + return + elif [[ "${keys:0:1}" == $'\e' ]]; then + zvm_exit_insert_mode false + ZVM_KEYS="${keys:1}${extra_keys}" + return + fi + ;; + visual) + ;; + esac + + ZVM_KEYS= +} + +# Read keys for retrieving and executing a widget +function zvm_readkeys_handler() { + local keymap=${1} + local keys=${2:-$KEYS} + local key= + local widget= + + # Get the keymap if keymap is empty + if [[ -z $keymap ]]; then + case "$ZVM_MODE" in + $ZVM_MODE_INSERT) keymap=viins;; + $ZVM_MODE_NORMAL) keymap=vicmd;; + $ZVM_MODE_VISUAL|$ZVM_MODE_VISUAL_LINE) keymap=visual;; + esac + fi + + # Read keys and retrieve the widget + zvm_readkeys $keymap $keys + keys=${retval[1]} + widget=${retval[2]} + key=${retval[3]} + + # Escape space in keys + keys=${keys//$ZVM_ESCAPE_SPACE/ } + key=${key//$ZVM_ESCAPE_SPACE/ } + + ZVM_KEYS="${keys}" + + # If the widget is current handler, we should call the default handler + if [[ "${widget}" == "${funcstack[1]}" ]]; then + widget= + fi + + # If the widget isn't matched, we should call the default handler + if [[ -z ${widget} ]]; then + # Disable reset prompt action, as multiple calling this function + # will cause potential line eaten issue. + ZVM_RESET_PROMPT_DISABLED=true + + zle zvm_default_handler "$key" + + ZVM_RESET_PROMPT_DISABLED=false + + # Push back to the key input stack, and postpone reset prompt + if [[ -n "$ZVM_KEYS" ]]; then + # To prevent ZLE from error "not enough arguments for -U", the + # parameter should be put after `--` symbols. + zle -U -- "${ZVM_KEYS}" + else + # If there is any reset prompt, we need to execute for + # prompt resetting. + zvm_postpone_reset_prompt false + fi + else + zle $widget + fi + + ZVM_KEYS= +} + +# Find and move cursor to next character +function zvm_find_and_move_cursor() { + local char=$1 + local count=${2:-1} + local forward=${3:-true} + local skip=${4:-false} + local cursor=$CURSOR + + [[ -z $char ]] && return 1 + + # Find the specific character + while :; do + if $forward; then + cursor=$((cursor+1)) + ((cursor > $#BUFFER)) && break + else + cursor=$((cursor-1)) + ((cursor < 0)) && break + fi + if [[ ${BUFFER[$cursor+1]} == $char ]]; then + count=$((count-1)) + fi + ((count == 0)) && break + done + + [[ $count > 0 ]] && return 1 + + # Skip the character + if $skip; then + if $forward; then + cursor=$((cursor-1)) + else + cursor=$((cursor+1)) + fi + fi + + CURSOR=$cursor +} + +# Handle the navigation action +function zvm_navigation_handler() { + # Return if no keys provided + [[ -z $1 ]] && return 1 + + local keys=$1 + local count= + local cmd= + + # Retrieve the calling command + if [[ $keys =~ '^([1-9][0-9]*)?([fFtT].?)$' ]]; then + count=${match[1]:-1} + + # The length of keys must be 2 + if (( ${#match[2]} < 2)); then + zvm_enter_oppend_mode + + read -k 1 cmd + keys+=$cmd + + case "$(zvm_escape_non_printed_characters ${keys[-1]})" in + $ZVM_VI_OPPEND_ESCAPE_BINDKEY) return 1;; + esac + + zvm_exit_oppend_mode + fi + + local forward=true + local skip=false + + [[ ${keys[-2]} =~ '[FT]' ]] && forward=false + [[ ${keys[-2]} =~ '[tT]' ]] && skip=true + + # Escape special characters (e.g. ', ", `, ~, ^, |, &, ) + local key=${keys[-1]} + if [[ $key =~ "['\\\"\`\~\^\|\#\&\*\;\}\(\)\<\>\ ]" ]]; then + key=\\${key} + fi + + cmd=(zvm_find_and_move_cursor $key $count $forward $skip) + count=1 + # Handle G command + elif [[ $keys =~ '^([1-9][0-9]*)?G$' ]]; then + count=${match[1]:-1} + cmd=(CURSOR=$#BUFFER) + # Handle gg command + elif [[ $keys =~ '^([1-9][0-9]*)?gg$' ]]; then + count=${match[1]:-1} + cmd=(CURSOR=0) + else + count=${keys:0:-1} + case ${keys: -1} in + '^') cmd=(zle vi-first-non-blank);; + '$') cmd=(zle vi-end-of-line);; + ' ') cmd=(zle vi-forward-char);; + '0') cmd=(zle vi-digit-or-beginning-of-line);; + 'h') cmd=(zle vi-backward-char);; + 'j') cmd=(zle down-line-or-history);; + 'k') cmd=(zle up-line-or-history);; + 'l') cmd=(zle vi-forward-char);; + 'w') cmd=(zle vi-forward-word);; + 'W') cmd=(zle vi-forward-blank-word);; + 'e') cmd=(zle vi-forward-word-end);; + 'E') cmd=(zle vi-forward-blank-word-end);; + 'b') cmd=(zle vi-backward-word);; + 'B') cmd=(zle vi-backward-blank-word);; + esac + fi + + # Check widget if the widget is empty + if [[ -z $cmd ]]; then + return 0 + fi + + # Check if keys includes the count + if [[ ! $count =~ ^[0-9]+$ ]]; then + count=1 + fi + + zvm_repeat_command "$cmd" $count + local exit_code=$? + + if [[ $exit_code == 0 ]]; then + retval=$keys + fi + + return $exit_code +} + +# Handle a range of characters +function zvm_range_handler() { + local keys=$1 + local cursor=$CURSOR + local key= + local mode= + local cmds=($ZVM_MODE) + local count=1 + local exit_code=$ZVM_RANGE_HANDLER_RET_OK + + # Enter operator pending mode + zvm_enter_oppend_mode false + + # If the keys is less than 2 keys, we should read more + # keys (e.g. d, c, y, etc.) + while (( ${#keys} < 2 )); do + zvm_update_cursor + read -k 1 key + keys="${keys}${key}" + done + + # If the keys ends in numbers, we should read more + # keys (e.g. d2, c3, y10, etc.) + while [[ ${keys: 1} =~ ^[1-9][0-9]*$ ]]; do + zvm_update_cursor + read -k 1 key + keys="${keys}${key}" + done + + # If the 2nd character is `i` or `a`, we should read + # one more key + if [[ ${keys} =~ '^.[ia]$' ]]; then + zvm_update_cursor + read -k 1 key + keys="${keys}${key}" + elif [[ ${keys} =~ '^.g$' ]]; then + # If the 2nd character is `g`, we should also read + # one more key for `gg` + zvm_update_cursor + read -k 1 key + keys="${keys}${key}" + fi + + # Exit operator pending mode + zvm_exit_oppend_mode + + # Handle escape in operator pending mode + # escape non-printed characters (e.g. ^[) + if [[ $(zvm_escape_non_printed_characters "$keys") =~ + ${ZVM_VI_OPPEND_ESCAPE_BINDKEY/\^\[/\\^\\[} ]]; then + return $ZVM_RANGE_HANDLER_RET_CANCEL + fi + + # Enter visual mode or visual line mode + if [[ $ZVM_MODE != $ZVM_MODE_VISUAL && + $ZVM_MODE != $ZVM_MODE_VISUAL_LINE ]]; then + case "${keys}" in + [cdy][jk]) mode=$ZVM_MODE_VISUAL_LINE;; + cc|dd|yy) mode=$ZVM_MODE_VISUAL_LINE;; + *) mode=$ZVM_MODE_VISUAL;; + esac + # Select the mode + if [[ ! -z $mode ]]; then + zvm_select_vi_mode $mode false + fi + fi + + ####################################### + # Selection Cases: + # + # 1. SAMPLE: `word1 word2 w`, CURSOR: at `w` of `word1` + # + # c[we] -> `word1` + # c2[we] -> `word1 word2` + # ve -> `word1` + # v2e -> `word1 word2` + # vw -> `word1 w` + # v2w -> `word1 word2 w` + # [dy]e -> `word1` + # [dy]2e -> `word1 word2` + # [dy]w -> `word1 ` + # [dy]2w -> `word1 word2 ` + # [cdyv]iw -> `word1` + # [cdyv]aw -> `word1 ` + # [cdyv]2iw -> `word1 ` + # [cdyv]2aw -> `word1 word2 ` + # + # 2. SAMPLE: `a bb c dd`, CURSOR: at `a` + # + # cw -> `a` + # c2w -> `a bb` + # ce -> `a bb` + # c2e -> `a bb c` + # + # 3. SAMPLE: ` .foo. bar. baz.`, CURSOR: at `f` + # + # c[WE] -> `foo.` + # c2[WE] -> `foo. bar.` + # vE -> `foo.` + # v2E -> `foo. bar.` + # vW -> `foo. b` + # v2W -> `foo. bar. b` + # d2W -> `foo. bar. b` + # [dy]E -> `foo.` + # [dy]2E -> `foo. bar.` + # [dy]W -> `foo. ` + # [dy]2W -> `foo. bar. ` + # [cdyv]iW -> `.foo.` + # [cdyv]aW -> `.foo. ` + # [cdyv]2iW -> `.foo. ` + # [cdyv]2aW -> `.foo. bar. ` + # + # 4. SAMPLE: ` .foo.bar.baz.`, CURSOR: at `r` + # + # [cdy]b -> `ba` + # [cdy]B -> `.foo.ba` + # vb -> `bar` + # vB -> `.foo.bar` + # vFf -> `foo.bar` + # vTf -> `oo.bar` + # [cdyv]fz -> `r.baz` + # [cdy]Ff -> `foo.ba` + # [cdyv]tz -> `r.ba` + # [cdy]Tf -> `oo.ba` + # + + # Pre navigation handling + local navkey= + + if [[ $keys =~ '^c([1-9][0-9]*)?[ia][wW]$' ]]; then + count=${match[1]:-1} + navkey=${keys: -2} + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?[ia][eE]$' ]]; then + navkey= + elif [[ $keys =~ '^c([1-9][0-9]*)?[eEwW]$' ]]; then + count=${match[1]:-1} + navkey=c${keys: -1} + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?[bB]$' ]]; then + MARK=$((MARK-1)) + count=${match[1]:-1} + navkey=${keys: -1} + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?([FT].?)$' ]]; then + MARK=$((MARK-1)) + count=${match[1]:-1} + navkey=${match[2]} + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?j$' ]]; then + # Exit if there is no line below + count=${match[1]:-1} + local i= + for ((i=$((CURSOR+1)); i<=$#BUFFER; i++)); do + [[ ${BUFFER[$i]} == $'\n' ]] && navkey='j' + done + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?k$' ]]; then + # Exit if there is no line above + count=${match[1]:-1} + local i= + for ((i=$((CURSOR+1)); i>0; i--)); do + [[ ${BUFFER[$i]} == $'\n' ]] && navkey='k' + done + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?[\^h0]$' ]]; then + MARK=$((MARK-1)) + count=${match[1]:-1} + navkey=${keys: -1} + + # Exit if the cursor is at the beginning of a line + if ((MARK < 0)); then + navkey= + elif [[ ${BUFFER[$MARK+1]} == $'\n' ]]; then + navkey= + fi + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?l$' ]]; then + count=${match[1]:-1} + count=$((count-1)) + navkey=${count}l + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?G$' ]]; then + count=${match[1]:-1} + navkey=G + elif [[ $keys =~ '^[cdy]([1-9][0-9]*)?gg$' ]]; then + MARK=$((MARK-1)) + count=${match[1]:-1} + navkey=gg + elif [[ $keys =~ '^.([1-9][0-9]*)?([^0-9]+)$' ]]; then + count=${match[1]:-1} + navkey=${match[2]} + else + navkey= + fi + + # Handle navigation + case $navkey in + '') exit_code=$ZVM_RANGE_HANDLER_RET_CONTINUE;; + *[ia]?) + # At least 1 time + if [[ -z $count ]]; then + count=1 + fi + + # Retrieve the widget + cmd= + case ${navkey: -2} in + iw) cmd=(zle select-in-word);; + aw) cmd=(zle select-a-word);; + iW) cmd=(zle select-in-blank-word);; + aW) cmd=(zle select-a-blank-word);; + esac + + if [[ -n "$cmd" ]]; then + zvm_repeat_command "$cmd" $count + elif [[ -n "$(zvm_match_surround "${keys[-1]}")" ]]; then + ZVM_KEYS="${keys}" + exit_code=$ZVM_RANGE_HANDLER_RET_PUSHBACK + elif [[ "${keys[1]}" == 'v' ]]; then + exit_code=$ZVM_RANGE_HANDLER_RET_CONTINUE + else + exit_code=$ZVM_RANGE_HANDLER_RET_CANCEL + fi + ;; + c[eEwW]) + ####################################### + # Selection Cases: + # + # 1. SAMPLE: `word1 word2 w`, CURSOR: at `1` of `word1` + # + # c[weWE] -> `1` + # c2[weWE] -> `1 word2` + # + # 2. SAMPLE: `word1 word2 w`, CURSOR: at ` ` after `word1` + # + # cw -> ` ` + # c2w -> ` word2 ` + # ce -> ` word2` + # c2e -> ` word2 w` + # + + if [[ "${BUFFER[$((CURSOR + 1))]}" == ' ' ]]; then + case ${navkey: -1} in + w) cmd=(zle vi-forward-word);; + W) cmd=(zle vi-forward-blank-word);; + e) cmd=(zle vi-forward-word-end);; + E) cmd=(zle vi-forward-blank-word-end);; + esac + + zvm_repeat_command "$cmd" $count + + case ${navkey: -1} in + w|W) CURSOR=$((CURSOR-1));; + esac + else + if [[ "${BUFFER[$((CURSOR + 2))]}" == ' ' ]]; then + count=$((count - 1)) + fi + + case ${navkey: -1} in + e|w) cmd=(zle vi-forward-word-end);; + E|W) cmd=(zle vi-forward-blank-word-end);; + esac + + zvm_repeat_command "$cmd" $count + fi + ;; + *) + local retval= + + # Prevent some actions(e.g. w, e) from affecting the auto + # suggestion suffix + BUFFER+=$'\0' + + if zvm_navigation_handler "${count}${navkey}"; then + keys="${keys[1]}${retval}" + else + exit_code=$ZVM_RANGE_HANDLER_RET_CONTINUE + fi + + BUFFER[-1]='' + ;; + esac + + # Check if there is no range selected + # For the exit code: + # 1) Loop in visual mode + # 2) Loop by ZVM_KEYS + # 3) Exit loop + if [[ $exit_code != 0 ]]; then + return $exit_code + fi + + # Post navigation handling + if [[ $keys =~ '^[cdy]([1-9][0-9]*)?[ia][wW]$' ]]; then + cursor=$MARK + elif [[ $keys =~ '[dy]([1-9][0-9]*)?[wW]' ]]; then + CURSOR=$((CURSOR-1)) + # If the CURSOR is at the newline character, we should + # move backward a character + if [[ "${BUFFER:$CURSOR:1}" == $'\n' ]]; then + CURSOR=$((CURSOR-1)) + fi + else + cursor=$CURSOR + fi + + # Handle operation + case "${keys}" in + c*) zvm_vi_change false; cursor=;; + d*) zvm_vi_delete false; cursor=;; + y*) zvm_vi_yank false; cursor=;; + [vV]*) cursor=;; + esac + + # Reset the repeat commands when it's changing or deleting + if $ZVM_REPEAT_MODE; then + zvm_exit_visual_mode false + elif [[ $keys =~ '^[cd].*' ]]; then + cmds+=($keys) + zvm_reset_repeat_commands $cmds + fi + + # Change the cursor position if the cursor is not null + if [[ ! -z $cursor ]]; then + CURSOR=$cursor + fi +} + +# Repeat executing command +function zvm_repeat_command { + local cmd=$1 + local count=${2:-1} + + # check if it's a zle command + local is_zle_cmd=false + if [[ ${cmd} =~ '^zle .*' ]]; then + is_zle_cmd=true + fi + + # Execute the command for `count` times. We can not use + # variable `i`, since some widgets will affect the variable + # `i`, and it will cause an infinite loop. + local init_cursor=$CURSOR + local last_cursor=$CURSOR + local exit_code=0 c= + for ((c=0; c operator, we should + # use >! operator to ignore the noclobber. + echo "$BUFFER" >! "$tmp_file" + + # Edit the file with the specific editor, in case of + # the warning about input not from a terminal (e.g. + # vim), we should tell the editor input is from the + # terminal and not from standard input. + "${(@Q)${(z)${ZVM_VI_EDITOR}}}" $tmp_file =0; bpos--)); do + if [[ "${buffer:$bpos:1}" == $'\n' ]]; then + _bpos=$((bpos+1)) + break + fi + done + + # Find the end of current line + for ((epos=$cursor; $epos<$#buffer; epos++)); do + if [[ "${buffer:$epos:1}" == $'\n' ]]; then + _epos=$epos + break + fi + done + + # Search for the URL or path + for ((bpos=$_bpos; $bpos<=$cursor; bpos++)); do + for ((epos=$((_epos-1)); $epos>=$cursor; epos--)); do + content=${buffer:$bpos:$((epos-bpos+1))} + if zvm_is_url "$content" || zvm_is_path "$content"; then + echo $bpos $epos + return + fi + done + done + + echo $cursor $cursor +} + +# Open URL or folder under cursor (gx command) +function zvm_open_under_cursor() { + # Get the word under the cursor + local ret=($(zvm_select_url_or_path $CURSOR $BUFFER)) + local bpos=${ret[1]} epos=${ret[2]} + local content=${BUFFER:$bpos:$((epos-bpos+1))} + + # Check if it's a valid URL + if zvm_is_url "$content"; then + # Open URL with default browser + if [[ -n $ZVM_OPEN_URL_CMD ]]; then + local -a cmd + cmd=("${(z)ZVM_OPEN_URL_CMD}") + "$cmd[@]" "$content" + elif zvm_exist_command "open"; then + open "$content" + elif zvm_exist_command "xdg-open"; then + xdg-open "$content" + fi + # Check if it's a valid path + elif zvm_is_path "$content"; then + if [[ -n $ZVM_OPEN_FILE_CMD ]]; then + local -a cmd + cmd=("${(z)ZVM_OPEN_FILE_CMD}") + "$cmd[@]" "$content" + elif zvm_exist_command "open"; then + open "$content" + elif zvm_exist_command "xdg-open"; then + xdg-open "$content" + fi + fi +} + +# Get the substr position in a string +function zvm_substr_pos() { + local pos=-1 + local len=${#1} + local slen=${#2} + local i=${3:-0} + local forward=${4:-true} + local init=${i:-$($forward && echo "$i" || echo "i=$len-1")} + local condition=$($forward && echo "i<$len" || echo "i>=0") + local step=$($forward && echo 'i++' || echo 'i--') + for (($init;$condition;$step)); do + if [[ ${1:$i:$slen} == "$2" ]]; then + pos=$i + break + fi + done + echo $pos +} + +# Parse surround from keys +function zvm_parse_surround_keys() { + local keys=${1:-${$(zvm_keys)//$ZVM_ESCAPE_SPACE/ }} + local action= + local surround= + case "${keys}" in + vS*) action=S; surround=${keys:2};; + vsa*) action=a; surround=${keys:3};; + vys*) action=y; surround=${keys:3};; + s[dr]*) action=${keys:1:1}; surround=${keys:2};; + [acd]s*) action=${keys:0:1}; surround=${keys:2};; + [cdvy][ia]*) action=${keys:0:2}; surround=${keys:2};; + esac + echo $action ${surround// /$ZVM_ESCAPE_SPACE} +} + +# Move around code structure (e.g. (..), {..}) +function zvm_move_around_surround() { + local slen= + local bpos=-1 + local epos=-1 + local i= s= + for ((i=$CURSOR;i>=0;i--)); do + # Check if it's one of the surrounds + for s in {\',\",\`,\(,\[,\{,\<}; do + slen=${#s} + if [[ ${BUFFER:$i:$slen} == "$s" ]]; then + bpos=$i + break + fi + done + if (($bpos == -1)); then + continue + fi + # Search the nearest surround + local ret=($(zvm_search_surround "$s")) + if [[ -z ${ret[@]} ]]; then + continue + fi + bpos=${ret[1]} + epos=${ret[2]} + # Move between the opening and close surrounds + if (( $CURSOR > $((bpos-1)) )) && (( $CURSOR < $((bpos+slen)) )); then + CURSOR=$epos + else + CURSOR=$bpos + fi + break + done +} + +# Match the surround pair from the part +function zvm_match_surround() { + local bchar=${1// /$ZVM_ESCAPE_SPACE} + local echar=$bchar + case $bchar in + '(') echar=')';; + '[') echar=']';; + '{') echar='}';; + '<') echar='>';; + ')') bchar='(';echar=')';; + ']') bchar='[';echar=']';; + '}') bchar='{';echar='}';; + '>') bchar='<';echar='>';; + "'") ;; + '"') ;; + '`') ;; + *) return;; + esac + echo $bchar $echar +} + +# Search surround from the string +function zvm_search_surround() { + local ret=($(zvm_match_surround "$1")) + local bchar=${${ret[1]//$ZVM_ESCAPE_SPACE/ }:- } + local echar=${${ret[2]//$ZVM_ESCAPE_SPACE/ }:- } + local bpos=$(zvm_substr_pos $BUFFER $bchar $CURSOR false) + local epos=$(zvm_substr_pos $BUFFER $echar $CURSOR true) + if [[ $bpos == $epos ]]; then + epos=$(zvm_substr_pos $BUFFER $echar $((CURSOR+1)) true) + if [[ $epos == -1 ]]; then + epos=$(zvm_substr_pos $BUFFER $echar $((CURSOR-1)) false) + if [[ $epos != -1 ]]; then + local tmp=$epos; epos=$bpos; bpos=$tmp + fi + fi + fi + if [[ $bpos == -1 ]] || [[ $epos == -1 ]]; then + return + fi + echo $bpos $epos $bchar $echar +} + +# Select surround and highlight it in visual mode +function zvm_select_surround() { + local ret=($(zvm_parse_surround_keys)) + local action=${1:-${ret[1]}} + local surround=${2:-${ret[2]//$ZVM_ESCAPE_SPACE/ }} + ret=($(zvm_search_surround ${surround})) + if [[ ${#ret[@]} == 0 ]]; then + zvm_exit_visual_mode + return + fi + local bpos=${ret[1]} + local epos=${ret[2]} + if [[ ${action:1:1} == 'i' ]]; then + ((bpos++)) + else + ((epos++)) + fi + MARK=$bpos; CURSOR=$epos-1 + + # refresh for highlight redraw + zle redisplay +} + +# Change surround in vicmd or visual mode +function zvm_change_surround() { + local ret=($(zvm_parse_surround_keys)) + local action=${1:-${ret[1]}} + local surround=${2:-${ret[2]//$ZVM_ESCAPE_SPACE/ }} + local bpos=${3} epos=${4} + local is_appending=false + case $action in + S|y|a) is_appending=true;; + esac + if $is_appending; then + if [[ -z $bpos && -z $epos ]]; then + ret=($(zvm_selection)) + bpos=${ret[1]} epos=${ret[2]} + fi + else + ret=($(zvm_search_surround "$surround")) + (( ${#ret[@]} )) || return + bpos=${ret[1]} epos=${ret[2]} + zvm_highlight custom $bpos $(($bpos+1)) + zvm_highlight custom $epos $(($epos+1)) + fi + local key= + case $action in + c|r) + zvm_enter_oppend_mode + read -k 1 key + zvm_exit_oppend_mode + ;; + S|y|a) + if [[ -z $surround ]]; then + zvm_enter_oppend_mode + read -k 1 key + zvm_exit_oppend_mode + else + key=$surround + fi + if [[ $ZVM_MODE == $ZVM_MODE_VISUAL ]]; then + zle visual-mode + fi + ;; + esac + + # Check if it is ESCAPE key ( or ZVM_VI_ESCAPE_BINDKEY) + case "$key" in + $'\e'|"${ZVM_VI_ESCAPE_BINDKEY//\^\[/$'\e'}") + zvm_highlight clear + return + esac + + # Start changing surround + ret=($(zvm_match_surround "$key")) + local bchar=${${ret[1]//$ZVM_ESCAPE_SPACE/ }:-$key} + local echar=${${ret[2]//$ZVM_ESCAPE_SPACE/ }:-$key} + local value=$($is_appending && echo 0 || echo 1 ) + local head=${BUFFER:0:$bpos} + local body=${BUFFER:$((bpos+value)):$((epos-(bpos+value)))} + local foot=${BUFFER:$((epos+value))} + BUFFER="${head}${bchar}${body}${echar}${foot}" + + # Clear highliht + zvm_highlight clear + + case $action in + S|y|a) zvm_select_vi_mode $ZVM_MODE_NORMAL;; + esac +} + +# Change surround text object +function zvm_change_surround_text_object() { + local ret=($(zvm_parse_surround_keys)) + local action=${1:-${ret[1]}} + local surround=${2:-${ret[2]//$ZVM_ESCAPE_SPACE/ }} + ret=($(zvm_search_surround "${surround}")) + if [[ ${#ret[@]} == 0 ]]; then + zvm_select_vi_mode $ZVM_MODE_NORMAL + return + fi + local bpos=${ret[1]} + local epos=${ret[2]} + if [[ ${action:1:1} == 'i' ]]; then + ((bpos++)) + else + ((epos++)) + fi + CUTBUFFER=${BUFFER:$bpos:$(($epos-$bpos))} + case ${action:0:1} in + c) + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$bpos + zvm_select_vi_mode $ZVM_MODE_INSERT + ;; + d) + BUFFER="${BUFFER:0:$bpos}${BUFFER:$epos}" + CURSOR=$bpos + ;; + esac +} + +# Repeat last change +function zvm_repeat_change() { + local times=${NUMERIC:-1} + ZVM_REPEAT_MODE=true + ZVM_RESET_PROMPT_DISABLED=true + + local cmd=${ZVM_REPEAT_COMMANDS[2]} + + # Handle repeat command for the specified number of times + local i= + for ((i=0; i<$times; i++)); do + case $cmd in + [aioAIO]) zvm_repeat_insert;; + c) zvm_repeat_vi_change;; + [cd]*) zvm_repeat_range_change;; + R) zvm_repeat_replace;; + r) zvm_repeat_replace_chars;; + *) zle vi-repeat-change;; + esac + done + + zle redisplay + + ZVM_RESET_PROMPT_DISABLED=false + ZVM_REPEAT_MODE=false +} + +# Repeat inserting characters +function zvm_repeat_insert() { + local cmd=${ZVM_REPEAT_COMMANDS[2]} + local cmds=(${ZVM_REPEAT_COMMANDS[3,-1]}) + + # Pre-handle the command + case $cmd in + a) CURSOR+=1;; + o) + zle vi-backward-char + zle vi-end-of-line + LBUFFER+=$'\n' + ;; + A) + zle vi-end-of-line + CURSOR=$((CURSOR+1)) + ;; + I) zle vi-first-non-blank;; + O) + zle vi-digit-or-beginning-of-line + LBUFFER+=$'\n' + CURSOR=$((CURSOR-1)) + ;; + esac + + # Insert characters + local i= + for ((i=1; i<=${#cmds[@]}; i++)); do + cmd="${cmds[$i]}" + + # Handle the backspace command + if [[ $cmd == '' ]]; then + if (($#LBUFFER > 0)); then + LBUFFER=${LBUFFER:0:-1} + fi + continue + fi + + # The length of character should be 1 + if (($#cmd == 1)); then + zvm_self_insert "$cmd" + fi + done +} + +# Repeat changing visual characters +function zvm_repeat_vi_change() { + local mode=${ZVM_REPEAT_COMMANDS[1]} + local cmds=(${ZVM_REPEAT_COMMANDS[3,-1]}) + + # Backward move cursor to the beginning of line + if [[ $mode == $ZVM_MODE_VISUAL_LINE ]]; then + zle vi-digit-or-beginning-of-line + fi + + local ncount=${cmds[1]} + local ccount=${cmds[2]} + local pos=$CURSOR epos=$CURSOR + local i= + + # Forward expand the characters to the Nth newline character + for ((i=0; i<$ncount; i++)); do + pos=$(zvm_substr_pos $BUFFER $'\n' $pos) + if [[ $pos == -1 ]]; then + epos=$#BUFFER + break + fi + pos=$((pos+1)) + epos=$pos + done + + # Forward expand the remaining characters + for ((i=0; i<$ccount; i++)); do + local char=${BUFFER[$epos+i]} + if [[ $char == $'\n' || $char == '' ]]; then + ccount=$i + break + fi + done + + epos=$((epos+ccount)) + RBUFFER=${RBUFFER:$((epos-CURSOR))} +} + +# Repeat changing a range of characters +function zvm_repeat_range_change() { + local cmd=${ZVM_REPEAT_COMMANDS[2]} + + # Remove characters + zvm_range_handler $cmd + + # Insert characters + zvm_repeat_insert +} + +# Repeat replacing +function zvm_repeat_replace() { + local cmds=(${ZVM_REPEAT_COMMANDS[3,-1]}) + local cmd= + local cursor=$CURSOR + local i= + + for ((i=1; i<=${#cmds[@]}; i++)); do + cmd="${cmds[$i]}" + + # If the cmd or the character at cursor is a newline character, + # or the cursor is at the end of buffer, we should insert the + # cmd instead of replacing with the cmd. + if [[ $cmd == $'\n' || + $BUFFER[$cursor+1] == $'\n' || + $BUFFER[$cursor+1] == '' + ]]; then + LBUFFER+=$cmd + else + BUFFER[$cursor+1]=$cmd + fi + + cursor=$((cursor+1)) + CURSOR=$cursor + done + + # The cursor position should go back one character after + # exiting the replace mode + zle vi-backward-char +} + +# Repeat replacing characters +function zvm_repeat_replace_chars() { + local mode=${ZVM_REPEAT_COMMANDS[1]} + local cmds=(${ZVM_REPEAT_COMMANDS[3,-1]}) + local cmd= + + # Replacement of visual mode should move backward cursor to the + # begin of current line, and replacing to the end of last line. + if [[ $mode == $ZVM_MODE_VISUAL_LINE ]]; then + zle vi-digit-or-beginning-of-line + cmds+=($'\n') + fi + + local cursor=$((CURSOR+1)) + local i= + + for ((i=1; i<=${#cmds[@]}; i++)); do + cmd="${cmds[$i]}" + + # If we meet a newline character in the buffer, we should keep + # stop replacing, util we meet next newline character command. + if [[ ${BUFFER[$cursor]} == $'\n' ]]; then + if [[ $cmd == $'\n' ]]; then + cursor=$((cursor+1)) + fi + continue + fi + + # A newline character command should keep replacing with last + # character, until we meet a newline character in the buffer, + # then we use next command. + if [[ $cmd == $'\n' ]]; then + i=$((i-1)) + cmd="${cmds[$i]}" + fi + + # The length of character should be 1 + if (($#cmd == 1)); then + BUFFER[$cursor]="${cmd}" + fi + + cursor=$((cursor+1)) + + # Break when it reaches the end + if ((cursor > $#BUFFER)); then + break + fi + done +} + +# Select a word under the cursor +function zvm_select_in_word() { + local cursor=${1:-$CURSOR} + local buffer=${2:-$BUFFER} + local bpos=$cursor epos=$cursor + local pattern='[0-9a-zA-Z_]' + + if ! [[ "${buffer:$cursor:1}" =~ $pattern ]]; then + pattern="[^${pattern:1:-1} ]" + fi + + for ((; $bpos>=0; bpos--)); do + [[ "${buffer:$bpos:1}" =~ $pattern ]] || break + done + for ((; $epos<$#buffer; epos++)); do + [[ "${buffer:$epos:1}" =~ $pattern ]] || break + done + + bpos=$((bpos+1)) + + # The ending position must be greater than 0 + if (( epos > 0 )); then + epos=$((epos-1)) + fi + + echo $bpos $epos +} + +# Switch keyword +function zvm_switch_keyword() { + local bpos= epos= cpos=$CURSOR + + # Cursor position cases: + # + # 1. Cursor on symbol: + # 2+2 => + + # 2-2 => - + # 2 + 2 => + + # 2 +2 => +2 + # 2 -2 => -2 + # 2 -a => -a + # + # 2. Cursor on number or alpha: + # 2+2 => +2 + # 2-2 => -2 + # 2 + 2 => 2 + # 2 +2 => +2 + # 2 -2 => -2 + # 2 -a => -a + + # If cursor is on the `+` or `-`, we need to check if it is a + # number with a sign or an operator, only the number needs to + # forward the cursor. + if [[ ${BUFFER:$cpos:2} =~ [+-][0-9] ]]; then + if [[ $cpos == 0 || ${BUFFER:$((cpos-1)):1} =~ [^0-9] ]]; then + cpos=$((cpos+1)) + fi + + # If cursor is on the `+` or `-`, we need to check if it is a + # short option, only the short option needs to forward the cursor. + elif [[ ${BUFFER:$cpos:2} =~ [+-][a-zA-Z] ]]; then + if [[ $cpos == 0 || ${BUFFER:$((cpos-1)):1} == ' ' ]]; then + cpos=$((cpos+1)) + fi + fi + + local result=($(zvm_select_in_word $cpos)) + bpos=${result[1]} epos=$((${result[2]}+1)) + + # Move backward the cursor + if [[ $bpos != 0 && ${BUFFER:$((bpos-1)):1} == [+-] ]]; then + bpos=$((bpos-1)) + fi + + local word=${BUFFER:$bpos:$((epos-bpos))} + local keys=$(zvm_keys) + + if [[ $keys == '' ]]; then + local increase=true + else + local increase=false + fi + + # Execute extra commands + for handler in $zvm_switch_keyword_handlers; do + if ! zvm_exist_command ${handler}; then + continue + fi + + result=($($handler $word $increase)); + + if (( $#result == 0 )); then + continue + fi + + epos=$(( bpos + ${result[3]} )) + bpos=$(( bpos + ${result[2]} )) + + if (( cpos < bpos )) || (( cpos >= epos )); then + continue + fi + + # Save to history and only keep some recent records + zvm_switch_keyword_history+=("${handler}:${word}") + zvm_switch_keyword_history=("${zvm_switch_keyword_history[@]: -10}") + + BUFFER="${BUFFER:0:$bpos}${result[1]}${BUFFER:$epos}" + CURSOR=$((bpos + ${#result[1]} - 1)) + + zle reset-prompt + return + done +} + +# Switch number keyword +function zvm_switch_number { + local word=$1 + local increase=${2:-true} + local result= bpos= epos= + + # Hexadecimal + if [[ $word =~ [^0-9]?(0[xX][0-9a-fA-F]*) ]]; then + local number=${match[1]} + local prefix=${number:0:2} + bpos=$((mbegin-1)) epos=$mend + + # Hexadecimal cases: + # + # 1. Increment: + # 0xDe => 0xdf + # 0xdE => 0xDF + # 0xde0 => 0xddf + # 0xffffffffffffffff => 0x0000000000000000 + # 0X9 => 0XA + # 0Xdf => 0Xe0 + # + # 2. Decrement: + # 0xdE0 => 0xDDF + # 0xffFf0 => 0xfffef + # 0xfffF0 => 0xFFFEF + # 0x0 => 0xffffffffffffffff + # 0X0 => 0XFFFFFFFFFFFFFFFF + # 0Xf => 0Xe + + local lower=true + if [[ $number =~ [A-Z][0-9]*$ ]]; then + lower=false + fi + + # Fix the number truncated after 15 digits issue + if (( $#number > 17 )); then + local d=$(($#number - 15)) + local h=${number:0:$d} + number="0x${number:$d}" + fi + + local p=$(($#number - 2)) + + if $increase; then + if (( $number == 0x${(l:15::f:)} )); then + h=$(([##16]$h+1)) + h=${h: -1} + number=${(l:15::0:)} + else + h=${h:2} + number=$(([##16]$number + 1)) + fi + else + if (( $number == 0 )); then + if (( ${h:-0} == 0 )); then + h=f + else + h=$(([##16]$h-1)) + h=${h: -1} + fi + number=${(l:15::f:)} + else + h=${h:2} + number=$(([##16]$number - 1)) + fi + fi + + # Padding with zero + if (( $#number < $p )); then + number=${(l:$p::0:)number} + fi + + result="${h}${number}" + + # Transform the case + if $lower; then + result="${(L)result}" + fi + + result="${prefix}${result}" + + # Binary + elif [[ $word =~ [^0-9]?(0[bB][01]*) ]]; then + # Binary cases: + # + # 1. Increment: + # 0b1 => 0b10 + # 0x1111111111111111111111111111111111111111111111111111111111111111 => + # 0x0000000000000000000000000000000000000000000000000000000000000000 + # 0B0 => 0B1 + # + # 2. Decrement: + # 0b1 => 0b0 + # 0b100 => 0b011 + # 0B010 => 0B001 + # 0b0 => + # 0x1111111111111111111111111111111111111111111111111111111111111111 + + local number=${match[1]} + local prefix=${number:0:2} + bpos=$((mbegin-1)) epos=$mend + + # Fix the number truncated after 63 digits issue + if (( $#number > 65 )); then + local d=$(($#number - 63)) + local h=${number:0:$d} + number="0b${number:$d}" + fi + + local p=$(($#number - 2)) + + if $increase; then + if (( $number == 0b${(l:63::1:)} )); then + h=$(([##2]$h+1)) + h=${h: -1} + number=${(l:63::0:)} + else + h=${h:2} + number=$(([##2]$number + 1)) + fi + else + if (( $number == 0b0 )); then + if (( ${h:-0} == 0 )); then + h=1 + else + h=$(([##2]$h-1)) + h=${h: -1} + fi + number=${(l:63::1:)} + else + h=${h:2} + number=$(([##2]$number - 1)) + fi + fi + + # Padding with zero + if (( $#number < $p )); then + number=${(l:$p::0:)number} + fi + + result="${prefix}${number}" + + # Decimal + elif [[ $word =~ ([-+]?[0-9]+) ]]; then + # Decimal cases: + # + # 1. Increment: + # 0 => 1 + # 99 => 100 + # + # 2. Decrement: + # 0 => -1 + # 10 => 9 + # aa1230xa => aa1231xa + # aa1230bb => aa1231bb + # aa123a0bb => aa124a0bb + + local number=${match[1]} + bpos=$((mbegin-1)) epos=$mend + + if $increase; then + result=$(($number + 1)) + else + result=$(($number - 1)) + fi + + # Check if need the plus sign prefix + if [[ ${word:$bpos:1} == '+' ]]; then + result="+${result}" + fi + fi + + if [[ $result ]]; then + echo $result $bpos $epos + fi +} + +# Switch boolean keyword +function zvm_switch_boolean() { + local word=$1 + local increase=$2 + local result= + local bpos=0 epos=$#word + + # Remove option prefix + if [[ $word =~ (^[+-]{0,2}) ]]; then + local prefix=${match[1]} + bpos=$mend + word=${word:$bpos} + fi + + case ${(L)word} in + true) result=false;; + false) result=true;; + yes) result=no;; + no) result=yes;; + on) result=off;; + off) result=on;; + y) result=n;; + n) result=y;; + t) result=f;; + f) result=t;; + *) return;; + esac + + # Transform the case + if [[ $word =~ ^[A-Z]+$ ]]; then + result=${(U)result} + elif [[ $word =~ ^[A-Z] ]]; then + result=${(U)result:0:1}${result:1} + fi + + echo $result $bpos $epos +} + +# Switch weekday keyword +function zvm_switch_weekday() { + local word=$1 + local increase=$2 + local result=${(L)word} + local weekdays=( + sunday + monday + tuesday + wednesday + thursday + friday + saturday + ) + + local i=1 + + for ((; i<=${#weekdays[@]}; i++)); do + if [[ ${weekdays[i]:0:$#result} == ${result} ]]; then + result=${weekdays[i]} + break + fi + done + + # Return if no match + if (( i > ${#weekdays[@]} )); then + return + fi + + if $increase; then + if (( i == ${#weekdays[@]} )); then + i=1 + else + i=$((i+1)) + fi + else + if (( i == 1 )); then + i=${#weekdays[@]} + else + i=$((i-1)) + fi + fi + + # Abbreviation + if (( $#result == $#word )); then + result=${weekdays[i]} + else + result=${weekdays[i]:0:$#word} + fi + + # Transform the case + if [[ $word =~ ^[A-Z]+$ ]]; then + result=${(U)result} + elif [[ $word =~ ^[A-Z] ]]; then + result=${(U)result:0:1}${result:1} + fi + + echo $result 0 $#word +} + +# Switch operator keyword +function zvm_switch_operator() { + local word=$1 + local increase=$2 + local result= + + case ${(L)word} in + '&&') result='||';; + '||') result='&&';; + '++') result='--';; + '--') result='++';; + '==') result='!=';; + '!=') result='==';; + '===') result='!==';; + '!==') result='===';; + '+') result='-';; + '-') result='*';; + '*') result='/';; + '/') result='+';; + 'and') result='or';; + 'or') result='and';; + *) return;; + esac + + # Transform the case + if [[ $word =~ ^[A-Z]+$ ]]; then + result=${(U)result} + elif [[ $word =~ ^[A-Z] ]]; then + result=${(U)result:0:1}${result:1} + fi + + # Since the `echo` command can not print the character + # `-`, here we use `printf` command alternatively. + printf "%s 0 $#word" "${result}" +} + +# Switch month keyword +function zvm_switch_month() { + local word=$1 + local increase=$2 + local result=${(L)word} + local months=( + january + february + march + april + may + june + july + august + september + october + november + december + ) + + local i=1 + + for ((; i<=${#months[@]}; i++)); do + if [[ ${months[i]:0:$#result} == ${result} ]]; then + result=${months[i]} + break + fi + done + + # Return if no match + if (( i > ${#months[@]} )); then + return + fi + + if $increase; then + if (( i == ${#months[@]} )); then + i=1 + else + i=$((i+1)) + fi + else + if (( i == 1 )); then + i=${#months[@]} + else + i=$((i-1)) + fi + fi + + ##################### + # Abbreviation + local lastlen=0 + local last="${zvm_switch_keyword_history[-1]}" + local funcmark="${funcstack[1]}:" + if [[ "$last" =~ "^${funcmark}" ]]; then + lastlen=$(($#last - $#funcmark)) + fi + + # Use cases: + # + # May -> June + # Apr -> May -> Jun + # April -> May -> June + # January -> Feb(Munual changing) -> Mar + # Jan -> February(Munual changing) -> March + # + if [[ "$result" == "may" ]]; then + if (($lastlen == 3)); then + result=${months[i]:0:3} + else + result=${months[i]} + fi + else + if (($#word == 3)); then + result=${months[i]:0:3} + else + result=${months[i]} + fi + fi + + ##################### + # Transform the case + if [[ $word =~ ^[A-Z]+$ ]]; then + result=${(U)result} + elif [[ $word =~ ^[A-Z] ]]; then + result=${(U)result:0:1}${result:1} + fi + + echo $result 0 $#word +} + +# Highlight content +function zvm_highlight() { + local opt=${1:-mode} + local region=() + local redraw=false + + # Handle region by the option + case "$opt" in + mode) + case "$ZVM_MODE" in + $ZVM_MODE_VISUAL|$ZVM_MODE_VISUAL_LINE) + local ret=($(zvm_calc_selection)) + local bpos=$((ret[1])) epos=$((ret[2])) + local bg=$ZVM_VI_HIGHLIGHT_BACKGROUND + local fg=$ZVM_VI_HIGHLIGHT_FOREGROUND + local es=$ZVM_VI_HIGHLIGHT_EXTRASTYLE + region=("$bpos $epos fg=$fg,bg=$bg,$es") + ;; + esac + redraw=true + ;; + custom) + local bpos=$2 epos=$3 + local bg=${4:-$ZVM_VI_HIGHLIGHT_BACKGROUND} + local fg=${5:-$ZVM_VI_HIGHLIGHT_FOREGROUND} + local es=${6:-$ZVM_VI_HIGHLIGHT_EXTRASTYLE} + region=("${ZVM_REGION_HIGHLIGHT[@]}") + region+=("$bpos $epos fg=$fg,bg=$bg,$es") + redraw=true + ;; + clear) + zle redisplay + redraw=true + ;; + redraw) redraw=true;; + esac + + # Update region highlight + if (( $#region > 0 )) || [[ "$opt" == 'clear' ]]; then + + # Remove old region highlight + local rawhighlight=() + local i= j= + for ((i=1; i<=${#region_highlight[@]}; i++)); do + local raw=true + local spl=(${(@s/ /)region_highlight[i]}) + local pat="${spl[1]} ${spl[2]}" + for ((j=1; j<=${#ZVM_REGION_HIGHLIGHT[@]}; j++)); do + if [[ "$pat" == "${ZVM_REGION_HIGHLIGHT[j]:0:$#pat}" ]]; then + raw=false + break + fi + done + if $raw; then + rawhighlight+=("${region_highlight[i]}") + fi + done + + # Assign new region highlight + ZVM_REGION_HIGHLIGHT=("${region[@]}") + region_highlight=("${rawhighlight[@]}" "${ZVM_REGION_HIGHLIGHT[@]}") + fi + + # Check if we need to refresh the region highlight + if $redraw; then + zle -R + fi +} + +# Enter the visual mode +function zvm_enter_visual_mode() { + local mode= + local last_mode=$ZVM_MODE + local last_region= + + # Exit the visual mode + case $last_mode in + $ZVM_MODE_VISUAL|$ZVM_MODE_VISUAL_LINE) + last_region=($MARK $CURSOR) + zvm_exit_visual_mode + ;; + esac + + case "${1:-$(zvm_keys)}" in + v) mode=$ZVM_MODE_VISUAL;; + V) mode=$ZVM_MODE_VISUAL_LINE;; + *) mode=$last_mode;; + esac + + # We should just exit the visual mode if current mode + # is the same with last visual mode + if [[ $last_mode == $mode ]]; then + return + fi + + zvm_select_vi_mode $mode + + # Recover the region when changing to another visual mode + if [[ -n $last_region ]]; then + MARK=$last_region[1] + CURSOR=$last_region[2] + zle redisplay + fi +} + +# Exit the visual mode +function zvm_exit_visual_mode() { + case "$ZVM_MODE" in + $ZVM_MODE_VISUAL) zle visual-mode;; + $ZVM_MODE_VISUAL_LINE) zle visual-line-mode;; + esac + zvm_highlight clear + zvm_select_vi_mode $ZVM_MODE_NORMAL ${1:-true} +} + +# Enter the vi insert mode +function zvm_enter_insert_mode() { + local keys=${1:-$(zvm_keys)} + + if [[ $keys == 'i' ]]; then + ZVM_INSERT_MODE='i' + elif [[ $keys == 'a' ]]; then + ZVM_INSERT_MODE='a' + if ! zvm_is_empty_line; then + CURSOR=$((CURSOR+1)) + fi + fi + + zvm_reset_repeat_commands $ZVM_MODE_NORMAL $ZVM_INSERT_MODE + zvm_select_vi_mode $ZVM_MODE_INSERT +} + +# Exit the vi insert mode +function zvm_exit_insert_mode() { + ZVM_INSERT_MODE= + zvm_select_vi_mode $ZVM_MODE_NORMAL ${1:-true} +} + +# Enter the vi operator pending mode +function zvm_enter_oppend_mode() { + ZVM_OPPEND_MODE=true + ${1:-true} && zvm_update_cursor +} + +# Exit the vi operator pending mode +function zvm_exit_oppend_mode() { + ZVM_OPPEND_MODE=false + ${1:-true} && zvm_update_cursor +} + +# Insert at the beginning of the line +function zvm_insert_bol() { + ZVM_INSERT_MODE='I' + zle vi-first-non-blank + zvm_exit_visual_mode false + zvm_select_vi_mode $ZVM_MODE_INSERT + zvm_reset_repeat_commands $ZVM_MODE_NORMAL $ZVM_INSERT_MODE +} + +# Append at the end of the line +function zvm_append_eol() { + ZVM_INSERT_MODE='A' + zle vi-end-of-line + zvm_exit_visual_mode false + zvm_select_vi_mode $ZVM_MODE_INSERT + zvm_reset_repeat_commands $ZVM_MODE_NORMAL $ZVM_INSERT_MODE +} + +# Self insert content to cursor position +function zvm_self_insert() { + local keys=${1:-$KEYS} + + # Update the autosuggestion + if [[ ${POSTDISPLAY:0:$#keys} == $keys ]]; then + POSTDISPLAY=${POSTDISPLAY:$#keys} + else + POSTDISPLAY= + fi + + LBUFFER+=${keys} +} + +# Reset the repeat commands +function zvm_reset_repeat_commands() { + ZVM_REPEAT_RESET=true + ZVM_REPEAT_COMMANDS=($@) +} + +# Select vi mode +function zvm_select_vi_mode() { + local mode=$1 + local reset_prompt=${2:-true} + + # Check if current mode is the same with the new mode + if [[ $mode == "$ZVM_MODE" ]]; then + zvm_update_cursor + return + fi + + zvm_exec_commands 'before_select_vi_mode' + + # Some plugins would reset the prompt when we select the + # keymap, so here we postpone executing reset-prompt. + zvm_postpone_reset_prompt true + + # Exit operator pending mode + if $ZVM_OPPEND_MODE; then + zvm_exit_oppend_mode false + fi + + case $mode in + $ZVM_MODE_NORMAL) + ZVM_MODE=$ZVM_MODE_NORMAL + zvm_update_cursor + zle vi-cmd-mode + ;; + $ZVM_MODE_INSERT) + ZVM_MODE=$ZVM_MODE_INSERT + zvm_update_cursor + zle vi-insert + ;; + $ZVM_MODE_VISUAL) + ZVM_MODE=$ZVM_MODE_VISUAL + zvm_update_cursor + zle visual-mode + ;; + $ZVM_MODE_VISUAL_LINE) + ZVM_MODE=$ZVM_MODE_VISUAL_LINE + zvm_update_cursor + zle visual-line-mode + ;; + $ZVM_MODE_REPLACE) + ZVM_MODE=$ZVM_MODE_REPLACE + zvm_enter_oppend_mode + ;; + esac + + # This aspect provides you a moment to do something, such as + # update the cursor, prompt and so on. + zvm_exec_commands 'after_select_vi_mode' + + # Stop and trigger reset-prompt + $reset_prompt && zvm_postpone_reset_prompt false true + + # Start the lazy keybindings when the first time entering the + # normal mode, when the mode is the same as last mode, we get + # empty value for $mode. + if [[ $mode == $ZVM_MODE_NORMAL ]] && + (( $#ZVM_LAZY_KEYBINDINGS_LIST > 0 )); then + + zvm_exec_commands 'before_lazy_keybindings' + + # Here we should unset the list for normal keybindings + local list=("${ZVM_LAZY_KEYBINDINGS_LIST[@]}") + unset ZVM_LAZY_KEYBINDINGS_LIST + + for r in "${list[@]}"; do + eval "zvm_bindkey ${r}" + done + + zvm_exec_commands 'after_lazy_keybindings' + fi +} + +# Postpone reset prompt +function zvm_postpone_reset_prompt() { + local toggle=$1 + local force=${2:-false} + + if $force; then + ZVM_POSTPONE_RESET_PROMPT=1 + fi + + if $toggle; then + ZVM_POSTPONE_RESET_PROMPT=0 + else + if (($ZVM_POSTPONE_RESET_PROMPT > 0)); then + ZVM_POSTPONE_RESET_PROMPT=-1 + zle reset-prompt + else + ZVM_POSTPONE_RESET_PROMPT=-1 + fi + fi +} + +# Reset prompt +function zvm_reset_prompt() { + if (($ZVM_POSTPONE_RESET_PROMPT >= 0)); then + ZVM_POSTPONE_RESET_PROMPT=$(($ZVM_POSTPONE_RESET_PROMPT + 1)) + return + fi + if [[ $ZVM_RESET_PROMPT_DISABLED == true ]]; then + return + fi + local -i retval=0 + if [[ -z "$rawfunc" ]]; then + zle .reset-prompt -- "$@" + retval=$? + else + $rawfunc -- "$@" + retval=$? + fi + return $retval +} + +# Undo action in vi insert mode +# +# CTRL-U Remove all characters between the cursor position and +# the beginning of the line. Previous versions of vim +# deleted all characters on the line. +function zvm_viins_undo() { + if $ZVM_VI_INSERT_MODE_LEGACY_UNDO; then + zvm_kill_line + else + zvm_backward_kill_line + fi +} + +function zvm_set_cursor() { + # Term of vim isn't supported + if [[ -n $VIM ]]; then + return + fi + + echo -ne "$1" +} + +# Get the escape sequence of cursor style +function zvm_cursor_style() { + local style=${(L)1} + local term=${2:-$ZVM_TERM} + + case $term in + # For xterm and rxvt and their derivatives use the same escape + # sequences as the VT520 terminal. And screen, konsole, alacritty, + # st and foot implement a superset of VT100 and VT100, they support + # 256 colors the same way xterm does. + xterm*|rxvt*|screen*|tmux*|konsole*|alacritty*|st*|foot*|wezterm) + case $style in + $ZVM_CURSOR_BLOCK) style='\e[2 q';; + $ZVM_CURSOR_UNDERLINE) style='\e[4 q';; + $ZVM_CURSOR_BEAM) style='\e[6 q';; + $ZVM_CURSOR_BLINKING_BLOCK) style='\e[1 q';; + $ZVM_CURSOR_BLINKING_UNDERLINE) style='\e[3 q';; + $ZVM_CURSOR_BLINKING_BEAM) style='\e[5 q';; + $ZVM_CURSOR_USER_DEFAULT) style='\e[0 q';; + esac + ;; + *) style='\e[0 q';; + esac + + # Restore default cursor color + if [[ $style == '\e[0 q' ]]; then + local old_style= + + case $ZVM_MODE in + $ZVM_MODE_INSERT) old_style=$ZVM_INSERT_MODE_CURSOR;; + $ZVM_MODE_NORMAL) old_style=$ZVM_NORMAL_MODE_CURSOR;; + $ZVM_MODE_VISUAL) old_style=$ZVM_VISUAL_MODE_CURSOR;; + $ZVM_MODE_VISUAL_LINE) old_style=$ZVM_VISUAL_LINE_MODE_CURSOR;; + esac + + if [[ $old_style =~ '\e\][0-9]+;.+\a' ]]; then + style=$style'\e\e]112\a' + fi + fi + + echo $style +} + +# Update the cursor according current vi mode +function zvm_update_cursor() { + + # Check if we need to update the cursor style + $ZVM_CURSOR_STYLE_ENABLED || return + + local mode=$1 + local shape= + + # Check if it is operator pending mode + if $ZVM_OPPEND_MODE; then + mode=opp + shape=$(zvm_cursor_style $ZVM_OPPEND_MODE_CURSOR) + fi + + # Get cursor shape by the mode + case "${mode:-$ZVM_MODE}" in + $ZVM_MODE_NORMAL) + shape=$(zvm_cursor_style $ZVM_NORMAL_MODE_CURSOR) + ;; + $ZVM_MODE_INSERT) + shape=$(zvm_cursor_style $ZVM_INSERT_MODE_CURSOR) + ;; + $ZVM_MODE_VISUAL) + shape=$(zvm_cursor_style $ZVM_VISUAL_MODE_CURSOR) + ;; + $ZVM_MODE_VISUAL_LINE) + shape=$(zvm_cursor_style $ZVM_VISUAL_LINE_MODE_CURSOR) + ;; + esac + + if [[ $shape ]]; then + zvm_set_cursor $shape + fi +} + +# Updates highlight region +function zvm_update_highlight() { + case "$ZVM_MODE" in + $ZVM_MODE_VISUAL|$ZVM_MODE_VISUAL_LINE) + zvm_highlight + ;; + esac +} + +# Updates repeat commands +function zvm_update_repeat_commands() { + # We don't need to update the repeat commands if current + # mode is already the repeat mode. + $ZVM_REPEAT_MODE && return + + # We don't need to update the repeat commands if it is + # resetting the repeat commands. + if $ZVM_REPEAT_RESET; then + ZVM_REPEAT_RESET=false + return + fi + + # We update the repeat commands when it's the insert mode + [[ $ZVM_MODE == $ZVM_MODE_INSERT ]] || return + + local char=$KEYS + + # If current key is an arrow key, we should do something + if [[ "$KEYS" =~ '\[[ABCD]' ]]; then + # If last key is also an arrow key, we just replace it + if [[ ${ZVM_REPEAT_COMMANDS[-1]} =~ '\[[ABCD]' ]]; then + ZVM_REPEAT_COMMANDS=(${ZVM_REPEAT_COMMANDS[@]:0:-1}) + fi + else + # If last command is arrow key movement, we should reset + # the repeat commands with i(nsert) command + if [[ ${ZVM_REPEAT_COMMANDS[-1]} =~ '\[[ABCD]' ]]; then + zvm_reset_repeat_commands $ZVM_MODE_NORMAL i + fi + char=${BUFFER[$CURSOR]} + fi + + # If current key is backspace key, we should remove last + # one, until it has only the mode and initial command + if [[ "$KEYS" == '' ]]; then + if ((${#ZVM_REPEAT_COMMANDS[@]} > 2)) && + [[ ${ZVM_REPEAT_COMMANDS[-1]} != '' ]]; then + ZVM_REPEAT_COMMANDS=(${ZVM_REPEAT_COMMANDS[@]:0:-1}) + elif (($#LBUFFER > 0)); then + ZVM_REPEAT_COMMANDS+=($KEYS) + fi + else + ZVM_REPEAT_COMMANDS+=($char) + fi +} + +# Clipboard support +function zvm_clipboard_detect() { + if [[ -n $ZVM_CLIPBOARD_COPY_CMD && -n $ZVM_CLIPBOARD_PASTE_CMD ]]; then + return + fi + if zvm_exist_command pbcopy && zvm_exist_command pbpaste; then + ZVM_CLIPBOARD_COPY_CMD='pbcopy' + ZVM_CLIPBOARD_PASTE_CMD='pbpaste' + return + fi + if zvm_exist_command wl-copy && zvm_exist_command wl-paste; then + ZVM_CLIPBOARD_COPY_CMD='wl-copy' + ZVM_CLIPBOARD_PASTE_CMD='wl-paste -n' + return + fi + if zvm_exist_command xclip; then + ZVM_CLIPBOARD_COPY_CMD='xclip -selection clipboard' + ZVM_CLIPBOARD_PASTE_CMD='xclip -selection clipboard -o' + return + fi + if zvm_exist_command xsel; then + ZVM_CLIPBOARD_COPY_CMD='xsel --clipboard -i' + ZVM_CLIPBOARD_PASTE_CMD='xsel --clipboard -o' + return + fi +} + +# Check if clipboard is available +function zvm_clipboard_available() { + zvm_clipboard_detect + if [[ -n $ZVM_CLIPBOARD_COPY_CMD && -n $ZVM_CLIPBOARD_PASTE_CMD ]]; then + return 0 + fi + return 1 +} + +# Copy CUTBUFFER to system clipboard +function zvm_clipboard_copy_buffer() { + $ZVM_SYSTEM_CLIPBOARD_ENABLED || return + zvm_clipboard_available || return + print -rn -- "$CUTBUFFER" | eval "$ZVM_CLIPBOARD_COPY_CMD" >/dev/null 2>&1 +} + +# Get content from system clipboard +function zvm_clipboard_get() { + zvm_clipboard_available || return + eval "$ZVM_CLIPBOARD_PASTE_CMD" 2>/dev/null +} + +# Paste content from system clipboard after cursor +function zvm_paste_clipboard_after() { + local content=$(zvm_clipboard_get) + [[ -z $content ]] && return + local saved=$CUTBUFFER + CUTBUFFER=$content + zvm_vi_put_after + CUTBUFFER=$saved +} + +# Paste content from system clipboard before cursor +function zvm_paste_clipboard_before() { + local content=$(zvm_clipboard_get) + [[ -z $content ]] && return + local saved=$CUTBUFFER + CUTBUFFER=$content + zvm_vi_put_before + CUTBUFFER=$saved +} + +# Paste content from system clipboard in visual mode +function zvm_visual_paste_clipboard() { + local content=$(zvm_clipboard_get) + if [[ -z $content ]]; then + zvm_exit_visual_mode + return + fi + local ret=($(zvm_calc_selection)) + local bpos=$ret[1] epos=$ret[2] + local cpos=$((bpos + $#content - 1)) + CUTBUFFER=${BUFFER:$bpos:$((epos-bpos))} + BUFFER="${BUFFER:0:$bpos}${content}${BUFFER:$epos}" + CURSOR=$cpos + zvm_exit_visual_mode +} + +# Updates editor information when line pre redraw +function zvm_zle-line-pre-redraw() { + # Fix cursor style is not updated in tmux environment, when + # there are one more panel in the same window, the program + # in other panel could change the cursor shape, we need to + # update cursor style when line is redrawing. + if [[ -n $TMUX ]]; then + zvm_update_cursor + # Fix display is not updated in the terminal of IntelliJ IDE. + [[ "$TERMINAL_EMULATOR" == "JetBrains-JediTerm" ]] && zle redisplay + fi + zvm_update_highlight + zvm_update_repeat_commands +} + +# Start every prompt in the correct vi mode +function zvm_zle-line-init() { + # Save last mode + local mode=${ZVM_MODE:-$ZVM_MODE_INSERT} + + # It's necessary to set to insert mode when line init + # and we don't need to reset prompt. + zvm_select_vi_mode $ZVM_MODE_INSERT false + + # Select line init mode and reset prompt + case ${ZVM_LINE_INIT_MODE:-$mode} in + $ZVM_MODE_INSERT) zvm_select_vi_mode $ZVM_MODE_INSERT;; + *) zvm_select_vi_mode $ZVM_MODE_NORMAL;; + esac +} + +# Restore the user default cursor style after prompt finish +function zvm_zle-line-finish() { + # When we start a program (e.g. vim, bash, etc.) from the + # command line, the cursor style is inherited by other + # programs, so that we need to reset the cursor style to + # default before executing a command and set the custom + # style again when the command exits. This way makes any + # other interactive CLI application would not be affected + # by it. + local shape=$(zvm_cursor_style $ZVM_CURSOR_USER_DEFAULT) + zvm_set_cursor $shape + zvm_switch_keyword_history=() +} + +# Initialize vi-mode for widgets, keybindings, etc. +function zvm_init() { + # Check if it has been initialized + if $ZVM_INIT_DONE; then + return; + fi + + # Mark plugin initial status + ZVM_INIT_DONE=true + + zvm_exec_commands 'before_init' + + # Correct the readkey engine + case $ZVM_READKEY_ENGINE in + $ZVM_READKEY_ENGINE_NEX|$ZVM_READKEY_ENGINE_ZLE);; + *) + echo -n "Warning: Unsupported readkey engine! " + echo "ZVM_READKEY_ENGINE=$ZVM_READKEY_ENGINE" + ZVM_READKEY_ENGINE=$ZVM_READKEY_ENGINE_DEFAULT + ;; + esac + + # Reduce ESC delay (zle default is 0.4 seconds) + # Set to 0.01 second delay for taking over the key input processing + case $ZVM_READKEY_ENGINE in + $ZVM_READKEY_ENGINE_NEX) KEYTIMEOUT=1;; + $ZVM_READKEY_ENGINE_ZLE) KEYTIMEOUT=$(($ZVM_KEYTIMEOUT*100));; + esac + + # Create User-defined widgets + zvm_define_widget zvm_default_handler + zvm_define_widget zvm_readkeys_handler + zvm_define_widget zvm_backward_kill_region + zvm_define_widget zvm_backward_kill_line + zvm_define_widget zvm_forward_kill_line + zvm_define_widget zvm_kill_line + zvm_define_widget zvm_viins_undo + zvm_define_widget zvm_select_surround + zvm_define_widget zvm_change_surround + zvm_define_widget zvm_move_around_surround + zvm_define_widget zvm_change_surround_text_object + zvm_define_widget zvm_enter_insert_mode + zvm_define_widget zvm_exit_insert_mode + zvm_define_widget zvm_enter_visual_mode + zvm_define_widget zvm_exit_visual_mode + zvm_define_widget zvm_enter_oppend_mode + zvm_define_widget zvm_exit_oppend_mode + zvm_define_widget zvm_exchange_point_and_mark + zvm_define_widget zvm_open_line_below + zvm_define_widget zvm_open_line_above + zvm_define_widget zvm_insert_bol + zvm_define_widget zvm_append_eol + zvm_define_widget zvm_self_insert + zvm_define_widget zvm_vi_replace + zvm_define_widget zvm_vi_replace_chars + zvm_define_widget zvm_vi_substitute + zvm_define_widget zvm_vi_substitute_whole_line + zvm_define_widget zvm_vi_change + zvm_define_widget zvm_vi_change_eol + zvm_define_widget zvm_vi_delete + zvm_define_widget zvm_vi_yank + zvm_define_widget zvm_vi_put_after + zvm_define_widget zvm_vi_put_before + zvm_define_widget zvm_vi_replace_selection + zvm_define_widget zvm_vi_up_case + zvm_define_widget zvm_vi_down_case + zvm_define_widget zvm_vi_opp_case + zvm_define_widget zvm_vi_edit_command_line + zvm_define_widget zvm_repeat_change + zvm_define_widget zvm_switch_keyword + zvm_define_widget zvm_paste_clipboard_after + zvm_define_widget zvm_paste_clipboard_before + zvm_define_widget zvm_visual_paste_clipboard + + # Open URL under cursor + zvm_define_widget zvm_open_under_cursor + + # Override standard widgets + zvm_define_widget zle-line-pre-redraw zvm_zle-line-pre-redraw + + # Ensure the correct cursor style when an interactive program + # (e.g. vim, bash, etc.) starts and exits + zvm_define_widget zle-line-init zvm_zle-line-init + zvm_define_widget zle-line-finish zvm_zle-line-finish + + # Override reset-prompt widget + zvm_define_widget reset-prompt zvm_reset_prompt + + # All Key bindings + # Emacs-like bindings + # Normal editing + zvm_bindkey viins '^A' beginning-of-line + zvm_bindkey viins '^E' end-of-line + zvm_bindkey viins '^B' backward-char + zvm_bindkey viins '^F' forward-char + zvm_bindkey viins '^K' zvm_forward_kill_line + zvm_bindkey viins '^W' backward-kill-word + zvm_bindkey viins '^U' zvm_viins_undo + zvm_bindkey viins '^Y' yank + zvm_bindkey viins '^_' undo + + # Mode agnostic editing + zvm_bindkey viins '^[[H' beginning-of-line + zvm_bindkey vicmd '^[[H' beginning-of-line + zvm_bindkey viins '^[[F' end-of-line + zvm_bindkey vicmd '^[[F' end-of-line + zvm_bindkey viins '^[[3~' delete-char + zvm_bindkey vicmd '^[[3~' delete-char + + # History search + zvm_bindkey viins '^R' history-incremental-search-backward + zvm_bindkey viins '^S' history-incremental-search-forward + zvm_bindkey viins '^P' up-line-or-history + zvm_bindkey viins '^N' down-line-or-history + + # Insert mode + zvm_bindkey vicmd 'i' zvm_enter_insert_mode + zvm_bindkey vicmd 'a' zvm_enter_insert_mode + zvm_bindkey vicmd 'I' zvm_insert_bol + zvm_bindkey vicmd 'A' zvm_append_eol + + # Other key bindings + zvm_bindkey vicmd 'v' zvm_enter_visual_mode + zvm_bindkey vicmd 'V' zvm_enter_visual_mode + zvm_bindkey visual 'o' zvm_exchange_point_and_mark + zvm_bindkey vicmd 'o' zvm_open_line_below + zvm_bindkey vicmd 'O' zvm_open_line_above + zvm_bindkey vicmd 'r' zvm_vi_replace_chars + zvm_bindkey vicmd 'R' zvm_vi_replace + zvm_bindkey vicmd 's' zvm_vi_substitute + zvm_bindkey vicmd 'S' zvm_vi_substitute_whole_line + zvm_bindkey vicmd 'C' zvm_vi_change_eol + zvm_bindkey visual 'c' zvm_vi_change + zvm_bindkey visual 'd' zvm_vi_delete + zvm_bindkey visual 'x' zvm_vi_delete + zvm_bindkey visual 'y' zvm_vi_yank + zvm_bindkey vicmd 'p' zvm_vi_put_after + zvm_bindkey vicmd 'P' zvm_vi_put_before + zvm_bindkey visual 'p' zvm_vi_replace_selection + zvm_bindkey visual 'P' zvm_vi_replace_selection + zvm_bindkey visual 'U' zvm_vi_up_case + zvm_bindkey visual 'u' zvm_vi_down_case + zvm_bindkey visual '~' zvm_vi_opp_case + zvm_bindkey visual 'v' zvm_vi_edit_command_line + zvm_bindkey vicmd '.' zvm_repeat_change + + # Open URL under cursor + zvm_bindkey vicmd 'gx' zvm_open_under_cursor + + # Clipboard support + zvm_bindkey vicmd 'gp' zvm_paste_clipboard_after + zvm_bindkey vicmd 'gP' zvm_paste_clipboard_before + zvm_bindkey visual 'gp' zvm_visual_paste_clipboard + zvm_bindkey visual 'gP' zvm_visual_paste_clipboard + + # Switch keyword + zvm_bindkey vicmd '^A' zvm_switch_keyword + zvm_bindkey vicmd '^X' zvm_switch_keyword + + # Keybindings for escape key and some specials + local exit_oppend_mode_widget= + local exit_insert_mode_widget= + local exit_visual_mode_widget= + local default_handler_widget= + + case $ZVM_READKEY_ENGINE in + $ZVM_READKEY_ENGINE_NEX) + exit_oppend_mode_widget=zvm_readkeys_handler + exit_insert_mode_widget=zvm_readkeys_handler + exit_visual_mode_widget=zvm_readkeys_handler + ;; + $ZVM_READKEY_ENGINE_ZLE) + exit_insert_mode_widget=zvm_exit_insert_mode + exit_visual_mode_widget=zvm_exit_visual_mode + default_handler_widget=zvm_default_handler + ;; + esac + + # Bind custom escape key + zvm_bindkey vicmd "$ZVM_VI_OPPEND_ESCAPE_BINDKEY" $exit_oppend_mode_widget + zvm_bindkey viins "$ZVM_VI_INSERT_ESCAPE_BINDKEY" $exit_insert_mode_widget + zvm_bindkey visual "$ZVM_VI_VISUAL_ESCAPE_BINDKEY" $exit_visual_mode_widget + + # Bind the default escape key if the escape key is not the default + case "$ZVM_VI_OPPEND_ESCAPE_BINDKEY" in + '^['|'\e') ;; + *) zvm_bindkey vicmd '^[' $exit_oppend_mode_widget;; + esac + case "$ZVM_VI_INSERT_ESCAPE_BINDKEY" in + '^['|'\e') ;; + *) zvm_bindkey viins '^[' $exit_insert_mode_widget;; + esac + case "$ZVM_VI_VISUAL_ESCAPE_BINDKEY" in + '^['|'\e') ;; + *) zvm_bindkey visual '^[' $exit_visual_mode_widget;; + esac + + # Bind and overwrite original y/d/c of vicmd + for c in {y,d,c}; do + zvm_bindkey vicmd "$c" $default_handler_widget + done + + # Surround text-object + # Enable surround text-objects (quotes, brackets) + local surrounds=() + + # Append brackets + for s in ${(s..)^:-'()[]{}<>'}; do + surrounds+=($s) + done + + # Append quotes + for s in {\',\",\`,\ ,'^['}; do + surrounds+=($s) + done + + # Surround key bindings + for s in $surrounds; do + for c in {a,i}${s}; do + zvm_bindkey visual "$c" zvm_select_surround + done + for c in {c,d,y}{a,i}${s}; do + zvm_bindkey vicmd "$c" zvm_change_surround_text_object + done + if [[ $ZVM_VI_SURROUND_BINDKEY == 's-prefix' ]]; then + for c in s{d,r}${s}; do + zvm_bindkey vicmd "$c" zvm_change_surround + done + for c in sa${s}; do + zvm_bindkey visual "$c" zvm_change_surround + done + else + for c in {d,c}s${s}; do + zvm_bindkey vicmd "$c" zvm_change_surround + done + for c in {S,ys}${s}; do + zvm_bindkey visual "$c" zvm_change_surround + done + fi + done + + # Moving around surrounds + zvm_bindkey vicmd '%' zvm_move_around_surround + + # Fix BACKSPACE was stuck in zsh + # Since normally '^?' (backspace) is bound to vi-backward-delete-char + zvm_bindkey viins '^?' backward-delete-char + + # Initialize ZVM_MODE value + case ${ZVM_LINE_INIT_MODE:-$ZVM_MODE_INSERT} in + $ZVM_MODE_INSERT) ZVM_MODE=$ZVM_MODE_INSERT;; + *) ZVM_MODE=$ZVM_MODE_NORMAL;; + esac + + # Enable vi keymap + bindkey -v + + zvm_exec_commands 'after_init' +} + +# Check if a command is existed +function zvm_exist_command() { + command -v "$1" >/dev/null +} + +# Execute commands +function zvm_exec_commands() { + local commands="zvm_${1}_commands" + commands=(${(P)commands}) + + # Execute the default command + if zvm_exist_command "zvm_$1"; then + eval "zvm_$1" ${@:2} + fi + + # Execute extra commands + for cmd in $commands; do + if zvm_exist_command ${cmd}; then + cmd="$cmd ${@:2}" + fi + eval $cmd + done +} + +# Generate system report +function zvm_system_report() { + # OS + local os_info= + case "$(uname -s)" in + Darwin) + local product="$(sw_vers -productName)" + local version="$(sw_vers -productVersion) ($(sw_vers -buildVersion))" + os_info="${product} ${version}" + ;; + *) os_info="$(uname -s) ($(uname -r) $(uname -v) $(uname -m) $(uname -o))";; + esac + + # Terminal Program + local term_info="${TERM_PROGRAM:-unknown} ${TERM_PROGRAM_VERSION:-unknown}" + term_info="${term_info} (${TERM})" + + # ZSH Frameworks + local zsh_frameworks=() + + if zvm_exist_command "omz"; then + zsh_frameworks+=("oh-my-zsh $(omz version)") + fi + + if zvm_exist_command "starship"; then + zsh_frameworks+=("$(starship --version | head -n 1)") + fi + + if zvm_exist_command "antigen"; then + zsh_frameworks+=("$(antigen version | head -n 1)") + fi + + if zvm_exist_command "zplug"; then + zsh_frameworks+=("zplug $(zplug --version | head -n 1)") + fi + + if zvm_exist_command "zinit"; then + # As `zinit version` information includes term style, in order + # to acquire the pure text, we need to elimindate all the escape + # sequences. + local version=$(zinit version \ + | head -n 1 \ + | sed -E $'s/(\033\[[a-zA-Z0-9;]+ ?m)//g') + zsh_frameworks+=("${version}") + fi + + # Shell + local shell=$SHELL + if [[ -z $shell ]]; then + shell=zsh + fi + + ################# + # System Report + ################# + print - "- Terminal program: ${term_info}" + print - "- Operating system: ${os_info}" + print - "- ZSH framework: ${(j:, :)zsh_frameworks}" + print - "- ZSH version: $($shell --version)" + print - "- ZVM version: $(zvm_version | head -n 1)" +} + +# Load config by calling the config function +if zvm_exist_command "$ZVM_CONFIG_FUNC"; then + $ZVM_CONFIG_FUNC +fi + +# Initialize this plugin according to the mode +case $ZVM_INIT_MODE in + sourcing) zvm_init;; + *) precmd_functions+=(zvm_init);; +esac + diff --git a/dot_zshrc b/dot_zshrc index af99f99..3388ff1 100644 --- a/dot_zshrc +++ b/dot_zshrc @@ -84,7 +84,6 @@ if [[ -e NEXT_TO_RUN ]] then cat NEXT_TO_RUN fi -export PATH=$PATH:/sbin/ export TERMINAL=wezterm export MPD_HOST=127.0.0.1 export MPD_PORT=6969 @@ -99,7 +98,7 @@ function reload_gtk_theme() { export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm -## By /home/libkyy/.local/share/flutter/bin/cache/flutter_tools.snapshot +export PATH=$PATH:/sbin/ export PATH=$PATH:$HOME/.emacs.d/bin export PATH=$PATH:$HOME/bin export PATH=$PATH:$HOME/.config/emacs/bin diff --git a/private_dot_config/mako/config b/private_dot_config/mako/config new file mode 100644 index 0000000..0cb13f6 --- /dev/null +++ b/private_dot_config/mako/config @@ -0,0 +1,59 @@ +## Mako configuration file + +# GLOBAL CONFIGURATION OPTIONS +max-history=100 +sort=-time + +# BINDING OPTIONS +on-button-left=dismiss +on-button-middle=none +on-button-right=dismiss-all +on-touch=dismiss +on-notify=exec mpv --volume=150 /usr/share/sounds/freedesktop/stereo/message.oga + +# STYLE OPTIONS +font=Pixel Code 10 +width=300 +height=100 +margin=10 +padding=15 +border-size=2 +border-radius=0 +icons=1 +max-icon-size=48 +icon-location=left +markup=1 +actions=1 +history=1 +text-alignment=left +default-timeout=5000 +ignore-timeout=0 +max-visible=5 +layer=overlay +anchor=top-center + +background-color=#282828 +text-color=#d9e0ee +border-color=#98971a +progress-color=over #89b4fa + +[urgency=low] +border-color=#98971a +default-timeout=2000 + +[urgency=normal] +border-color=#98971a +default-timeout=5000 + +[urgency=high] +border-color=#f38ba8 +text-color=#f38ba8 +default-timeout=0 + +[category=mpd] +border-color=#f9e2af +default-timeout=2000 +group-by=category + +[mode=dnd] +invisible=1 diff --git a/private_dot_config/sway/config b/private_dot_config/sway/config index e470596..a2dbe0e 100644 --- a/private_dot_config/sway/config +++ b/private_dot_config/sway/config @@ -33,9 +33,10 @@ include /etc/sway/config-vars.d/* # pos 1360 0 # } set $main DP-1 -set $second HDMI-A-4 +# set $second HDMI-A-4 +set $second DP-3 # set $TV HDMI-A-3 -output $second resolution 1360x768 pos 3130 100 +# output $second resolution 1680x1050 pos 3130 100 output $TV { mode 1360x768 disable @@ -46,10 +47,8 @@ pos 0 0 } focus output $main output $second { -resolution 1280x1024@60.020Hz pos 1920 0 +resolution 1680x1050 pos 1920 0 transform 270 -scale 0.85 -disable } diff --git a/private_dot_config/sway/config.d/misc.conf b/private_dot_config/sway/config.d/misc.conf index cb41516..25304ad 100644 --- a/private_dot_config/sway/config.d/misc.conf +++ b/private_dot_config/sway/config.d/misc.conf @@ -1,6 +1,6 @@ #The best hot key ever bindsym ctrl+$mod+v exec firefox https://www.youtube.com/watch?v=dQw4w9WgXcQ -bindsym $mod+i exec emacsclient -nc +bindsym $mod+i exec emacs # bindsym ctrl+$mods+b exec --no-startup-id ~/scripts/bing_chilling.sh #disable mouse binds #bindsym $mod+shift+a exec ~/scripts/mouse.sh -0.5 0 @@ -96,5 +96,9 @@ exec_always wpctl set-volume @DEFAULT_AUDIO_SINK@ 90% exec_always wpctl set-volume @DEFAULT_AUDIO_SOURCE@ 52% #Sway Notif Center -exec swaync -bindsym $mod+Shift+n exec swaync-client -t -sw +# exec swaync +# bindsym $mod+Shift+n exec swaync-client -t -sw + +#mako time +exec mako +bindsym $mod+Shift+n exec makoctl mode -t dnd diff --git a/private_dot_local/private_share/private_applications/chatterino.desktop b/private_dot_local/private_share/private_applications/chatterino.desktop new file mode 100644 index 0000000..fa617f0 --- /dev/null +++ b/private_dot_local/private_share/private_applications/chatterino.desktop @@ -0,0 +1,21 @@ +[Desktop Entry] + +# The type as listed above +Type=Application + + +# The name of the application +Name=Chatterino + +# A comment which can/will be used as a tooltip +Comment=Twitch Chat Client Awesome + + +# The executable of the application, possibly with arguments. +Exec=chatterino + +# The name of the icon that will be used to display this entry +Icon=emacs + +# Describes whether this application needs to be run in a terminal or not +Terminal=false