diff --git a/functions/geometry_vi b/functions/geometry_vi new file mode 100644 index 00000000..b3a24518 --- /dev/null +++ b/functions/geometry_vi @@ -0,0 +1,80 @@ +# geometry_vi - A geometry plugin to display the ZLE vi-mode state. + +geometry_vi() { + # Required for RPROMPT to render properly after submitting a command. + geometry::vi-get-mode +} + +# Re-render the prompt. +function geometry::vi-update-prompt() { + imode=$(geometry::vi-insert-mode) + nmode=$(geometry::vi-normal-mode) + if [[ $KEYMAP == vicmd ]]; then + PROMPT=${PROMPT//$imode/$nmode} + RPROMPT=${RPROMPT//$imode/$nmode} + else + PROMPT=${PROMPT//$nmode/$imode} + RPROMPT=${RPROMPT//$nmode/$imode} + fi +} + +# Render normal mode. +function geometry::vi-normal-mode() { + ansi ${GEOMETRY_VI_NORMAL_COLOR:-white} ${GEOMETRY_VI_NORMAL_MODE:-""} +} + +# Render insert mode. +function geometry::vi-insert-mode() { + ansi ${GEOMETRY_VI_INSERT_COLOR:-yellow} ${GEOMETRY_VI_INSERT_MODE:-""} +} + +# Return the currently active mode. +function geometry::vi-get-mode() { + imode=$(geometry::vi-insert-mode) + nmode=$(geometry::vi-normal-mode) + [[ $KEYMAP == vicmd ]] && echo $nmode || echo $imode +} + +# ZLE widget to trigger prompt updates. +function geometry::vi-draw-mode { + keymap=$(geometry::vi-get-mode) + geometry::vi-update-prompt + zle reset-prompt +} + + +# Skip initialization code if geometry_vi is not configured for rendering. +[[ ${GEOMETRY_RPROMPT[(ie)geometry_vi]} -gt ${#GEOMETRY_RPROMPT} ]] && [[ ${GEOMETRY_PROMPT[(ie)geometry_vi]} -gt ${#GEOMETRY_PROMPT} ]] && return + +# Bind the widgets required to perform proper prompt updates. +# Adapted from https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/zsh-syntax-highlighting.zsh#L295-L359 +local prefix="geometry_vi" +for w (zle-keymap-select zle-line-pre-redraw zle-line-init zle-line-finish); do + case ${widgets[$w]:-""} in + # Already Bound to geometry: Do nothing. + user:geometry-vi-draw) ;; + + # Existing user widget: Chain a call to it. + user:*) zle -N $prefix-$w ${widgets[$w]#*:} + # Dynamically create a function to call the previous widget. + eval "geometry::${(q)prefix}-${(q)w}() { geometry::vi-draw-mode; builtin zle ${(q)prefix}-${(q)w} -- \"\$@\" }" + zle -N $w geometry::$prefix-$w;; + + # Completion widgets: Ignore. + completion:*) ;; + + # Builtin widget: Chain a call to ".widget". + # Eval is required to get a closure ( + builtin) eval "geometry::${(q)prefix}-${(q)w}() { geometry::vi-draw-mode; builtin zle .${(q)w} -- \"\$@\" }" + zle -N $w _zsh_highlight_widget_$prefix-$w;; + + # Unbound widget: Bind directly. + *) + if [[ $w == zle-* ]] && (( ! ${+widgets[$w]} )); then + zle -N $w geometry::vi-draw-mode + else + # Default: unhandled case. + print -r -- >&2 "geometry::geometry_vi: unhandled ZLE widget ${(qq)w}" + fi + esac +done diff --git a/readme.md b/readme.md index c680315d..bcd788a5 100644 --- a/readme.md +++ b/readme.md @@ -108,6 +108,19 @@ GEOMETRY_GIT_GREP=ack # define which grep-like tool to use (By def GEOMETRY_GIT_NO_COMMITS_MESSAGE="" # hide the 'no commits' message in new repositories GEOMETRY_GIT_TIME_DETAILED=true # show full time (e.g. `12h 30m 53s`) instead of the coarsest interval (e.g. `12h`) ``` +### geometry_vi + +Adds a customizable vi mode indicator to the prompt which displays +whether the shell is in insert or normal mode. ZSH can be configured +to use vi-like bindings by calling `bindkey -v` either at the command +line or in your `.zshrc`. + +```shell +GEOMETRY_VI_INSERT_MODE="" # The textual indicator for insert-mode. +GEOMETRY_VI_INSERT_COLOR=yellow # The color of the insert-mode indicator. +GEOMETRY_VI_NORMAL_MODE="" # The textual indicator for normal-mode. +GEOMETRY_VINORMAL_COLOR=white # The color of the normal-mode indicator. +``` ![git_conflicts](/images/screenshots/git_conflicts.png)