Commit 4c292ea2 authored by Sebastian Gniazdowski's avatar Sebastian Gniazdowski
Browse files

Initial commit of Zsh Navigation Tools

parent 9c08641d
These are skeletons, configuration is read from ~/.config/znt/*
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# Hotlist
local hotlist
hotlist=(
/usr/share/zsh/site-functions
/usr/share/zsh
/usr/local/share/zsh/site-functions
/usr/local/share/zsh
/usr/local/bin
/usr/lib
)
# Suppress adding (to directory stack) directories visited by n-cd
# Value 0 is the default (directories will be added to dirstack)
local NCD_DONT_PUSHD=0
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# Colorize last segments of the paths
# (#s) is ^, (#e) is $, # is *, ## is + (comparing to regex)
local NLIST_COLORING_PATTERN="[a-zA-Z0-9 ._-]##/#(#e)"
local NLIST_COLORING_COLOR=$'\x1b[00;33m'
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# (#s) is ^, (#e) is $, # is *, ## is + (comparing to regex)
local NLIST_COLORING_PATTERN="[a-zA-Z0-9_]##"
local NLIST_COLORING_MATCH_MULTIPLE=0
# Which editor to use, zed or vared
# vared is the default
local feditor="zed"
# local feditor="vared"
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
local active_text=underline
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# Colorize first number column and last path segment
# This doesn't cover scripts named "[0-9]## *", which should be very rare
# (#s) is ^, (#e) is $, # is *, ## is + (comparing to regex)
# | is alternative, but only in ()
local NLIST_COLORING_PATTERN="((#s) #[0-9]## |[[][^]]#](#e)|[^ 0-9/?\\\\][^/\\\\]#(#e)|[^ /\\\\]##[^0-9/\\\\ ]##[^/\\\\]#(#e))"
local NLIST_COLORING_COLOR=$'\x1b[00;33m'
local NLIST_COLORING_MATCH_MULTIPLE=1
# Should the list (text, borders) be drawn in bold
# Value 1 is the default
local bold=1
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
# How should be current element of the list drawn. Possible values: reverse,
# underline. Default (without option set) is reverse
# On Linux virtual terminal this will be enforced to reverse (because of poor
# underline support on that terminal)
# local active_text=underline
This diff is collapsed.
# Zsh Navigation Tools
http://imageshack.com/a/img633/7967/ps6rKR.png
A tool generating a selectable curses-based list of elements that has access to
current Zsh session, i.e. has broad capabilities to work together with it.
That's n-list. The files n-cd, n-env, n-kill, etc. are applications of
the tool. Feature highlights include incremental multi-word searching, ANSI
coloring, unique mode, horizontal scroll, non-selectable elements, grepping and
various integrations with Zsh.
## History Widget
To have n-history as the incremental searcher bound to Ctrl-R copy znt-*
files into the */site-functions dir (unless you use Oh My Zsh) and
add:
autoload znt-history-widget
zle -N znt-history-widget
bindkey "^R" znt-history-widget
to .zshrc. This is done automatically when using Oh My Zsh. Two other
widgets exist, znt-cd-widget and znt-kill-widget, they can be too assigned
to key combinations:
zle -N znt-cd-widget
bindkey "^T" znt-cd-widget
zle -N znt-kill-widget
bindkey "^Y" znt-kill-widget
## Introduction
The tools are:
- n-aliases - browses aliases, relegates editing to vared
- n-cd - browses dirstack and bookmarked directories, allows to enter selected directory
- n-functions - browses functions, relegates editing to zed or vared
- n-history - browses history, allows to edit and run commands from it
- n-kill - browses processes list, allows to send signal to selected process
- n-env - browses environment, relegates editing to vared
- n-options - browses options, allows to toggle their state
- n-panelize - loads output of given command into the list for browsing
All tools support horizontal scroll with <,>, {,}, h,l or left and right
cursors. Other keys are:
- [,] - jump directory bookmarks in n-cd and typical signals in n-kill
- Ctrl-d, Ctrl-u - half page up or down
- Ctrl-p, Ctrl-n - previous and next (also done with vim's j,k)
- Ctrl-l - redraw of whole display
- g, G - beginning and end of the list
- Ctrl-o, o - enter uniq mode (no duplicate lines)
- / - start incremental search
- Enter - finish incremental search, retaining filter
- Esc - exit incremental search, clearing filter
- Ctrl-w (in incremental search) - delete whole word
- Ctrl-k (in incremental search) - delete whole line
## Programming
The function n-list is used as follows:
n-list {element1} [element2] ... [elementN]
This is all that is needed to be done to have the features like ANSI coloring,
incremental multi-word search, unique mode, horizontal scroll, non-selectable
elements (grepping is done outside n-list, see the tools for how it can be
done). To set up non-selectable entries add their indices into array
NLIST_NONSELECTABLE_ELEMENTS:
typeset -a NLIST_NONSELECTABLE_ELEMENTS
NLIST_NONSELECTABLE_ELEMENTS=( 1 )
Result is stored as $reply[REPLY] ($ isn't needed before REPLY because
of arithmetic context inside []). The returned array might be different from
input arguments as n-list can process them via incremental search or uniq
mode. $REPLY is the index in that possibly processed array. If $REPLY
equals -1 it means that no selection have been made (user quitted via q
key).
To set up entries that can be jumped to with [,] keys add their indices to
NLIST_HOP_INDEXES array:
typeset -a NLIST_HOP_INDEXES
NLIST_HOP_INDEXES=( 1 10 )
n-list can automatically colorize entries according to a Zsh pattern.
Following example will colorize all numbers with blue:
local NLIST_COLORING_PATTERN="[0-9]##"
local NLIST_COLORING_COLOR=$'\x1b[00;34m'
local NLIST_COLORING_END_COLOR=$'\x1b[0m'
local NLIST_COLORING_MATCH_MULTIPLE=1
n-list "This is a number 123" "This line too has a number: 456"
Blue is the default color, it doesn't have to be set. See zshexpn man page
for more information on Zsh patterns. Briefly, comparing to regular
expressions, (#s) is ^, (#e) is $, # is *, ## is +. Alternative
will work when in parenthesis, i.e. (a|b). BTW by using this method you can
colorize output of the tools, via their config files (check out e.g. n-cd.conf,
it uses this).
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-aliases` to .zshrc
#
# This function allows to choose an alias for edition with vared
#
# Uses n-list
emulate -L zsh
setopt extendedglob
zmodload zsh/curses
zmodload zsh/parameter
local IFS="
"
unset NLIST_COLORING_PATTERN
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-aliases.conf ] && . ~/.config/znt/n-aliases.conf
local list
local selected
NLIST_REMEMBER_STATE=0
list=( "${(@k)aliases}" )
list=( "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
if [ "$#list" -eq 0 ]; then
echo "No matching aliases"
return 1
fi
list=( "${(@i)list}" )
n-list "$list[@]"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
echo "Editing \`$selected':"
print -rs "vared aliases\\[$selected\\]"
vared aliases\[$selected\]
fi
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-cd` to .zshrc
#
# This function allows to choose a directory from pushd stack
#
# Uses n-list
emulate -L zsh
setopt extendedglob pushdignoredups
zmodload zsh/curses
local IFS="
"
# Unset before configuration is read
unset NLIST_COLORING_PATTERN
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-cd.conf ] && . ~/.config/znt/n-cd.conf
local list
local selected
NLIST_REMEMBER_STATE=0
list=( `dirs -p` )
list=( "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
[ "$#list" -eq 0 ] && echo "No matching directories"
if [ "$#hotlist" -ge 1 ]; then
typeset -a NLIST_NONSELECTABLE_ELEMENTS NLIST_HOP_INDEXES
local tmp_list_size="$#list"
NLIST_NONSELECTABLE_ELEMENTS=( $(( tmp_list_size+1 )) $(( tmp_list_size+2 )) )
list=( "$list[@]" "" $'\x1b[00;31m'"Hotlist"$'\x1b[00;00m': "$hotlist[@]" )
(( tmp_list_size+=3 ))
local middle_hop=$(( (tmp_list_size+$#list) / 2 ))
[[ "$middle_hop" -eq $tmp_list_size || "$middle_hop" -eq $#list ]] && middle_hop=""
[ "$tmp_list_size" -eq $#list ] && tmp_list_size=""
NLIST_HOP_INDEXES=( 1 $tmp_list_size $middle_hop $#list )
else
[ "$#list" -eq 0 ] && return 1
fi
n-list "${list[@]}"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
selected="${selected/#\~/$HOME}"
(( NCD_DONT_PUSHD )) && setopt NO_AUTO_PUSHD
cd "$selected"
(( NCD_DONT_PUSHD )) && setopt AUTO_PUSHD
# ZLE?
if [ "${(t)CURSOR}" = "integer-local-special" ]; then
zle -M "You have selected $selected"
else
echo "You have selected $selected"
fi
else
[ "${(t)CURSOR}" = "integer-local-special" ] && zle redisplay
fi
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-env` to .zshrc
#
# This function allows to choose an environment variable
# for edition with vared
#
# Uses n-list
emulate -L zsh
setopt extendedglob
unsetopt equals
zmodload zsh/curses
local IFS="
"
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-env.conf ] && . ~/.config/znt/n-env.conf
local list
local selected
NLIST_REMEMBER_STATE=0
list=( `env` )
list=( "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
if [ "$#list" -eq 0 ]; then
echo "No matching variables"
return 1
fi
list=( "${(@i)list}" )
n-list "$list[@]"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
selected="${selected%%=*}"
echo "Editing \`$selected':"
print -rs "vared \"$selected\""
vared "$selected"
fi
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-functions` to .zshrc
#
# This function allows to choose a function for edition with vared
#
# Uses n-list
emulate -L zsh
setopt extendedglob
zmodload zsh/curses
zmodload zsh/parameter
local IFS="
"
unset NLIST_COLORING_PATTERN
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-functions.conf ] && . ~/.config/znt/n-functions.conf
local list
local selected
NLIST_REMEMBER_STATE=0
list=( "${(@k)functions}" )
list=( "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
if [ "$#list" -eq 0 ]; then
echo "No matching functions"
return 1
fi
list=( "${(@i)list}" )
n-list "$list[@]"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
if [ "$feditor" = "zed" ]; then
echo "Editing \`$selected' (ESC ZZ or Ctrl-x-w to finish):"
autoload zed
print -rs "zed -f -- \"$selected\""
zed -f -- "$selected"
else
echo "Editing \`$selected':"
print -rs "vared functions\\[$selected\\]"
vared functions\[$selected\]
fi
fi
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-history` to .zshrc
#
# This function allows to browse Z shell's history and use the
# entries
#
# Uses n-list
emulate -L zsh
setopt extendedglob
zmodload zsh/curses
local IFS="
"
unset NLIST_COLORING_PATTERN
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-history.conf ] && . ~/.config/znt/n-history.conf
local list
local selected
NLIST_REMEMBER_STATE=0
list=( `builtin history -rn 1` )
list=( "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
if [ "$#list" -eq 0 ]; then
echo "No matching history entries"
return 1
fi
n-list "${list[@]}"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
# ZLE?
if [ "${(t)CURSOR}" = "integer-local-special" ]; then
zle redisplay
zle kill-whole-line
zle -U "$selected"
else
print -zr "$selected"
fi
else
[ "${(t)CURSOR}" = "integer-local-special" ] && zle redisplay
fi
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-kill` to .zshrc
#
# This function allows to choose a process and a signal to send to it
#
# Uses n-list
emulate -L zsh
setopt extendedglob
zmodload zsh/curses
local IFS="
"
[ -f ~/.config/znt/n-list.conf ] && . ~/.config/znt/n-list.conf
[ -f ~/.config/znt/n-kill.conf ] && . ~/.config/znt/n-kill.conf
typeset -A signals
signals=(
1 "1 - HUP"
2 "2 - INT"
3 "3 - QUIT"
6 "6 - ABRT"
9 "9 - KILL"
14 "14 - ALRM"
15 "15 - TERM"
17 "17 - STOP"
19 "19 - CONT"
)
local list
local selected
local signal
local -a signal_names
local title
NLIST_REMEMBER_STATE=0
typeset -a NLIST_NONSELECTABLE_ELEMENTS
NLIST_NONSELECTABLE_ELEMENTS=( 1 )
type ps 2>/dev/null 1>&2 || { echo >&2 "Error: \`ps' not found"; return 1 }
case "$(uname)" in
CYGWIN*) list=( `command ps -Wa` ) ;;
*) list=( `command ps -o pid,uid,command -A` ) ;;
esac
# Ask of PID
title=$'\x1b[00;31m'"${list[1]}"$'\x1b[00;00m\0'
shift list
list=( "$title" "${(@M)list:#(#i)*$1*}" )
local NLIST_GREP_STRING="$1"
if [ "$#list" -eq 1 ]; then
echo "No matching processes"
return 1
fi
n-list "$list[@]"
# Got answer? (could be Ctrl-C or 'q')
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
selected="${selected## #}"
pid="${selected%% *}"
# Now ask of signal
signal_names=( ${(vin)signals} )
typeset -a NLIST_HOP_INDEXES
NLIST_HOP_INDEXES=( 3 6 8 )
unset NLIST_COLORING_PATTERN
n-list $'\x1b[00;31mSelect signal:\x1b[00;00m' "$signal_names[@]"
if [ "$REPLY" -gt 0 ]; then
selected="$reply[REPLY]"
signal="${(k)signals[(r)$selected]}"
# ZLE?
if [ "${(t)CURSOR}" = "integer-local-special" ]; then
zle redisplay
zle kill-whole-line
zle -U "kill -$signal $pid"
else
print -zr "kill -$signal $pid"
fi
else
[ "${(t)CURSOR}" = "integer-local-special" ] && zle redisplay
fi
else
[ "${(t)CURSOR}" = "integer-local-special" ] && zle redisplay
fi
# vim: set filetype=zsh:
# $1, $2, ... - elements of the list
# $NLIST_NONSELECTABLE_ELEMENTS - array of indexes (1-based) that cannot be selected
# $REPLY is the output variable - contains index (1-based) or -1 when no selection
#
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-list` to .zshrc
#
# This function outputs a list of elements that can be
# navigated with keyboard. Uses curses library
emulate -LR zsh
setopt typesetsilent extendedglob noshortloops
_nlist_has_terminfo=0
zmodload zsh/curses
zmodload zsh/terminfo 2>/dev/null && _nlist_has_terminfo=1
trap "REPLY=-2; reply=(); return" TERM INT QUIT
trap "_nlist_exit" EXIT
# Drawing and input
autoload n-list-draw n-list-input
# Cleanup before any exit
_nlist_exit() {
setopt localoptions
setopt extendedglob
[[ "$REPLY" = -(#c0,1)[0-9]## ]] || REPLY="-1"
zcurses 2>/dev/null delwin inner
zcurses 2>/dev/null delwin main
zcurses 2>/dev/null refresh
zcurses end
_nlist_alternate_screen 0
_nlist_cursor_visibility 1
unset _nlist_has_terminfo
}
# Outputs a message in the bottom of the screen
_nlist_status_msg() {
# -1 for border, -1 for 0-based indexing
zcurses move main $(( term_height - 1 - 1 )) 2
zcurses clear main eol
zcurses string main "$1"
#status_msg_strlen is localized in caller
status_msg_strlen=$#1
}
# Prefer tput, then module terminfo
_nlist_cursor_visibility() {
if type tput 2>/dev/null 1>&2; then
[ "$1" = "1" ] && { tput cvvis; tput cnorm }
[ "$1" = "0" ] && tput civis
elif [ "$_nlist_has_terminfo" = "1" ]; then
[ "$1" = "1" ] && { [ -n $terminfo[cvvis] ] && echo -n $terminfo[cvvis];
[ -n $terminfo[cnorm] ] && echo -n $terminfo[cnorm] }
[ "$1" = "0" ] && [ -n $terminfo[civis] ] && echo -n $terminfo[civis]
fi
}
# Reason for this function is that on some systems
# smcup and rmcup are not knowing why left empty
_nlist_alternate_screen() {
[ "$_nlist_has_terminfo" -ne "1" ] && return
[[ "$1" = "1" && -n "$terminfo[smcup]" ]] && return
[[ "$1" = "0" && -n "$terminfo[rmcup]" ]] && return
case "$TERM" in
*rxvt*)
[ "$1" = "1" ] && echo -n $'\x1b7\x1b[?47h'
[ "$1" = "0" ] && echo -n $'\x1b[2J\x1b[?47l\x1b8'
;;
*)
[ "$1" = "1" ] && echo -n $'\x1b[?1049h'
[ "$1" = "0" ] && echo -n $'\x1b[?1049l'
# just to remember two other that work: $'\x1b7\x1b[r\x1b[?47h', $'\x1b[?47l\x1b8'
;;
esac
}
_nlist_compute_user_vars_difference() {
if [[ "${(t)NLIST_NONSELECTABLE_ELEMENTS}" != "array" &&
"${(t)NLIST_NONSELECTABLE_ELEMENTS}" != "array-local" ]]
then
last_element_difference=0
current_difference=0
else
last_element_difference=$#NLIST_NONSELECTABLE_ELEMENTS
current_difference=0
local idx
for idx in "${(n)NLIST_NONSELECTABLE_ELEMENTS[@]}"; do
[ "$idx" -le "$NLIST_CURRENT_IDX" ] && current_difference+=1 || break
done
fi
}
# List was processed, check if variables aren't off range
_nlist_verify_vars() {
[ "$NLIST_CURRENT_IDX" -gt "$last_element" ] && NLIST_CURRENT_IDX="$last_element"
[[ "$NLIST_CURRENT_IDX" -eq 0 && "$last_element" -ne 0 ]] && NLIST_CURRENT_IDX=1
(( NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=0+((NLIST_CURRENT_IDX-1)/page_height)*page_height+1 ))
}
# Compute the variables which are shown to the user
_nlist_setup_user_vars() {
if [ "$1" = "1" ]; then
# Basic values when there are no non-selectables
NLIST_USER_CURRENT_IDX="$NLIST_CURRENT_IDX"
NLIST_USER_LAST_ELEMENT="$last_element"
else
_nlist_compute_user_vars_difference
NLIST_USER_CURRENT_IDX=$(( NLIST_CURRENT_IDX - current_difference ))
NLIST_USER_LAST_ELEMENT=$(( last_element - last_element_difference ))
fi
}
_nlist_coloring_list_into_col_list() {
local col=$'\x1b[00;34m' reset=$'\x1b[0m'
[ -n "$NLIST_COLORING_COLOR" ] && col="$NLIST_COLORING_COLOR"
[ -n "$NLIST_COLORING_END_COLOR" ] && reset="$NLIST_COLORING_END_COLOR"
if [ "$NLIST_COLORING_MATCH_MULTIPLE" -eq 1 ]; then
col_list=( "${(@)list//(#mi)$~NLIST_COLORING_PATTERN/$col${MATCH}$reset}" )
else
col_list=( "${(@)list/(#mi)$~NLIST_COLORING_PATTERN/$col${MATCH}$reset}" )
fi
}
#
# Main code
#
# Check if there is proper input
if [ "$#" -lt 1 ]; then
echo "Usage: n-list element_1 ..."
return 1
fi
REPLY="-1"
reply=()
integer term_height="$LINES"
integer term_width="$COLUMNS"
if [[ "$term_height" -lt 1 || "$term_width" -lt 1 ]]; then
local stty_out=$( stty size )
term_height="${stty_out% *}"
term_width="${stty_out#* }"
fi
integer inner_height=term_height-3
integer inner_width=term_width-3
integer page_height=inner_height
integer page_width=inner_width
typeset -a list col_list disp_list
integer last_element=$#
local action
local final_key
integer selection
integer last_element_difference=0
integer current_difference=0
local prev_search_buffer=""
integer prev_uniq_mode=0
integer prev_start_idx=-1
# Ability to remember the list between calls
if [[ -z "$NLIST_REMEMBER_STATE" || "$NLIST_REMEMBER_STATE" -eq 0 || "$NLIST_REMEMBER_STATE" -eq 2 ]]; then
NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN=1
NLIST_CURRENT_IDX=1
NLIST_IS_SEARCH_MODE=0
NLIST_SEARCH_BUFFER=""
NLIST_TEXT_OFFSET=0
NLIST_IS_UNIQ_MODE=0
# Zero - because it isn't known, unless we
# confirm that first element is selectable
NLIST_USER_CURRENT_IDX=0
[[ ${NLIST_NONSELECTABLE_ELEMENTS[(r)1]} != 1 ]] && NLIST_USER_CURRENT_IDX=1
NLIST_USER_LAST_ELEMENT=$(( last_element - $#NLIST_NONSELECTABLE_ELEMENTS ))
# 2 is init once, then remember
[ "$NLIST_REMEMBER_STATE" -eq 2 ] && NLIST_REMEMBER_STATE=1
fi
if [ "$NLIST_START_IN_SEARCH_MODE" -eq 1 ]; then
NLIST_START_IN_SEARCH_MODE=0
NLIST_IS_SEARCH_MODE=1
fi
if [ "$NLIST_START_IN_UNIQ_MODE" -eq 1 ]; then
NLIST_START_IN_UNIQ_MODE=0
NLIST_IS_UNIQ_MODE=1
fi
_nlist_alternate_screen 1
zcurses init
zcurses delwin main 2>/dev/null
zcurses delwin inner 2>/dev/null
zcurses addwin main "$term_height" "$term_width" 0 0
zcurses addwin inner "$inner_height" "$inner_width" 1 2
zcurses bg main white/black
zcurses bg inner white/black
if [ "$NLIST_IS_SEARCH_MODE" -ne 1 ]; then
_nlist_cursor_visibility 0
fi
#
# Listening for input
#
local key keypad
# Clear input buffer
zcurses timeout main 0
zcurses input main key keypad
zcurses timeout main -1
key=""
keypad=""
list=( "$@" )
last_element="$#list"
integer is_colored=0
if [[ -z "$NLIST_SEARCH_BUFFER" && -n "$NLIST_COLORING_PATTERN" ]]; then
is_colored=1
_nlist_coloring_list_into_col_list
fi
while (( 1 )); do
# Do searching (filtering with string)
if [ -n "$NLIST_SEARCH_BUFFER" ]; then
# Compute new list, col_list ?
if [[ "$NLIST_SEARCH_BUFFER" != "$prev_search_buffer" || "$NLIST_IS_UNIQ_MODE" -ne "$prev_uniq_mode" ]]; then
prev_search_buffer="$NLIST_SEARCH_BUFFER"
prev_uniq_mode="$NLIST_IS_UNIQ_MODE"
# regenerating list -> regenerating disp_list
prev_start_idx=-1
# Take all elements, including duplicates and non-selectables
typeset +U list
list=( "$@" )
# Remove non-selectable elements
[ "$#NLIST_NONSELECTABLE_ELEMENTS" -gt 0 ] && for i in "${(nO)NLIST_NONSELECTABLE_ELEMENTS[@]}"; do
list[$i]=()
done
# Remove duplicates
[ "$NLIST_IS_UNIQ_MODE" -eq 1 ] && typeset -U list
last_element="$#list"
# Next do the filtering
local search_buffer="${NLIST_SEARCH_BUFFER%% ##}"
search_buffer="${search_buffer## ##}"
search_buffer="${search_buffer//(#m)[][*?|#~^()><\\]/\\$MATCH}"
if [ -n "$search_buffer" ]; then
# Patterns will be *foo*~^*bar* and foo|bar)
local search_pattern="${search_buffer// ##/*~^*}"
local colsearch_pattern="${search_buffer// ##/|}"
list=( "${(@M)list:#(#i)*$~search_pattern*}" )
last_element="$#list"
local red=$'\x1b[00;31m' reset=$'\x1b[00;00m'
col_list=( "${(@)list//(#mi)($~colsearch_pattern)/$red${MATCH}$reset}" )
else
col_list=( "$list[@]" )
fi
# Called after processing list
_nlist_verify_vars
fi
_nlist_setup_user_vars 1
integer end_idx=$(( NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN + page_height - 1 ))
[ "$end_idx" -gt "$last_element" ] && end_idx=last_element
if [ "$prev_start_idx" -ne "$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN" ]; then
prev_start_idx="$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN"
disp_list=( "${(@)col_list[NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN, end_idx]}" )
fi
# Output colored list
n-list-draw "$(( (NLIST_CURRENT_IDX-1) % page_height + 1 ))" \
"$page_height" "$page_width" 0 0 "$NLIST_TEXT_OFFSET" inner \
"$disp_list[@]"
else
# There is no search, but there was in previous loop
# OR
# Uniq mode was entered or left out
# -> compute new list (maybe also col_list)
if [[ -n "$prev_search_buffer" || "$NLIST_IS_UNIQ_MODE" -ne "$prev_uniq_mode" ]]; then
prev_search_buffer=""
prev_uniq_mode="$NLIST_IS_UNIQ_MODE"
# regenerating list -> regenerating disp_list
prev_start_idx=-1
# Take all elements, including duplicates and non-selectables
typeset +U list
list=( "$@" )
# Remove non-selectable elements only when in uniq mode
[ "$NLIST_IS_UNIQ_MODE" -eq 1 ] && [ "$#NLIST_NONSELECTABLE_ELEMENTS" -gt 0 ] &&
for i in "${(nO)NLIST_NONSELECTABLE_ELEMENTS[@]}"; do
list[$i]=()
done
# Remove duplicates when in uniq mode
[ "$NLIST_IS_UNIQ_MODE" -eq 1 ] && typeset -U list
# Apply coloring pattern (when not with search query)
is_colored=0
if [ -n "$NLIST_COLORING_PATTERN" ]; then
is_colored=1
_nlist_coloring_list_into_col_list
fi
last_element="$#list"
# Called after processing list
_nlist_verify_vars
fi
# "1" - shouldn't bother with non-selectables
_nlist_setup_user_vars "$NLIST_IS_UNIQ_MODE"
integer end_idx=$(( NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN + page_height - 1 ))
[ "$end_idx" -gt "$last_element" ] && end_idx=last_element
if [ "$is_colored" -eq 0 ]; then
if [ "$prev_start_idx" -ne "$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN" ]; then
prev_start_idx="$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN"
disp_list=( "${(@)list[NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN, end_idx]}" )
fi
else
if [ "$prev_start_idx" -ne "$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN" ]; then
prev_start_idx="$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN"
disp_list=( "${(@)col_list[NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN, end_idx]}" )
fi
fi
# Output the list
n-list-draw "$(( (NLIST_CURRENT_IDX-1) % page_height + 1 ))" \
"$page_height" "$page_width" 0 0 "$NLIST_TEXT_OFFSET" inner \
"$disp_list[@]"
fi
local status_msg_strlen
if [ "$NLIST_IS_SEARCH_MODE" = "1" ]; then
local _txt2=""
[ "$NLIST_IS_UNIQ_MODE" -eq 1 ] && _txt2="[-UNIQ-] "
_nlist_status_msg "${_txt2}Filtering with: ${NLIST_SEARCH_BUFFER// /+}"
elif [[ ${NLIST_NONSELECTABLE_ELEMENTS[(r)$NLIST_CURRENT_IDX]} != $NLIST_CURRENT_IDX ||
-n "$NLIST_SEARCH_BUFFER" || "$NLIST_IS_UNIQ_MODE" -eq 1 ]]; then
local _txt="" _txt2=""
[ -n "$NLIST_GREP_STRING" ] && _txt=" [$NLIST_GREP_STRING]"
[ "$NLIST_IS_UNIQ_MODE" -eq 1 ] && _txt2="[-UNIQ-] "
_nlist_status_msg "${_txt2}Current #$NLIST_USER_CURRENT_IDX (of #$NLIST_USER_LAST_ELEMENT entries)$_txt"
else
_nlist_status_msg ""
fi
zcurses border main
zcurses refresh main inner
zcurses move main $(( term_height - 1 - 1 )) $(( status_msg_strlen + 2 ))
# Wait for input
zcurses input main key keypad
# Get the special (i.e. "keypad") key or regular key
if [ -n "$key" ]; then
final_key="$key"
elif [ -n "$keypad" ]; then
final_key="$keypad"
else
_nlist_status_msg "Inproper input detected"
zcurses refresh main inner
fi
n-list-input "$NLIST_CURRENT_IDX" "$NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN" \
"$page_height" "$page_width" "$last_element" "$NLIST_TEXT_OFFSET" \
"$final_key" "$NLIST_IS_SEARCH_MODE" "$NLIST_SEARCH_BUFFER" \
"$NLIST_IS_UNIQ_MODE"
selection="$reply[1]"
action="$reply[2]"
NLIST_CURRENT_IDX="$reply[3]"
NLIST_FROM_WHAT_IDX_LIST_IS_SHOWN="$reply[4]"
NLIST_TEXT_OFFSET="$reply[5]"
NLIST_IS_SEARCH_MODE="$reply[6]"
NLIST_SEARCH_BUFFER="$reply[7]"
NLIST_IS_UNIQ_MODE="$reply[8]"
if [ "$action" = "SELECT" ]; then
REPLY="$selection"
reply=( "$list[@]" )
break
elif [ "$action" = "QUIT" ]; then
REPLY=-1
reply=( "$list[@]" )
break
elif [ "$action" = "REDRAW" ]; then
zcurses clear main redraw
zcurses clear inner redraw
fi
done
# vim: set filetype=zsh:
# Copy this file into /usr/share/zsh/site-functions/
# and add 'autoload n-list-draw` to .zshrc
#
# This is an internal function not for direct use
emulate -L zsh
zmodload zsh/curses
setopt typesetsilent extendedglob
_nlist_print_with_ansi() {
local win="$1" text="$2" out col chunk Xout
integer text_offset="$3" max_text_len="$4" text_len=0 no_match=0 nochunk_text_len to_skip_from_chunk to_chop_off_from_chunk before_len
# 1 - non-escaped text, 2 - first number in the escaped text, with ;
# 3 - second number, 4 - text after whole escape text
typeset -a c
c=( black red green yellow blue magenta cyan white )
while [[ -n "$text" && "$no_match" -eq 0 ]]; do
if [[ "$text" = (#b)([^$'\x1b']#)$'\x1b'\[([0-9](#c0,2))(#B)(\;|)(#b)([0-9](#c0,2))m(*) ]]; then
# Text for further processing
text="$match[4]"
# Text chunk to output now
out="$match[1]"
# Save color
col="$match[2]"
(( match[3] >= 30 && match[3] <= 37 )) && col="$match[3]"
else
out="$text"
no_match=1
fi
if [ -n "$out" ]; then
################ Expand tabs ################
chunk="$out"
before_len="$text_len"
Xout=""
while [ -n "$chunk" ]; do
[[ "$chunk" = (#b)([^$'\t']#)$'\t'(*) ]] && {
(( all_text_len=((before_len+${#match[1]})/8+1)*8 ))
Xout+="${(r:all_text_len-before_len:: :)match[1]}"
before_len+=all_text_len-before_len
chunk="$match[2]"
} || {
Xout+="$chunk"
break
}
done
#############################################
# Input text length without the current chunk
nochunk_text_len=text_len
# Input text length up to current chunk
text_len+="$#Xout"
# Should start displaying with this chunk?
# I.e. stop skipping left part of the input text?
if (( text_len > text_offset )); then
to_skip_from_chunk=text_offset-nochunk_text_len
# LEFT - is chunk off the left skip boundary? +1 for 1-based index in string
(( to_skip_from_chunk > 0 )) && Xout="${Xout[to_skip_from_chunk+1,-1]}"
# RIGHT - is text off the screen?
if (( text_len-text_offset > max_text_len )); then
to_chop_off_from_chunk=0+(text_len-text_offset)-max_text_len
Xout="${Xout[1,-to_chop_off_from_chunk-1]}"
fi
[ -n "$Xout" ] && zcurses string "$win" "$Xout"
fi
fi
if (( no_match == 0 )); then
if (( col >= 30 && col <= 37 )); then
zcurses attr "$win" $c[col-29]/black
elif [[ "$col" -eq 0 ]]; then
zcurses attr "$win" white/black
fi
fi
done
}
integer highlight="$1"
integer page_height="$2"
integer page_width="$3"
local y_offset="$4"
local x_offset="$5"
local text_offset="$6"
local win="$7"
shift 7
integer max_text_len=page_width-x_offset
[ "$bold" = "0" ] && bold="" || bold="+bold"
[[ "$active_text" = "underline" || "$active_text" = "reverse" ]] || active_text="reverse"
# With Linux terminal underline won't work properly
[ "$TERM" = "linux" ] && active_text="reverse"
integer max_idx=page_height
integer end_idx=max_idx
[ "$end_idx" -gt "$#" ] && end_idx="$#"
integer y=y_offset
zcurses attr "$win" $bold white/black
integer i text_len
local text
for (( i=1; i<=end_idx; i++ )); do
zcurses move "$win" $y "$x_offset"
[ "$i" = "$highlight" ] && zcurses attr "$win" +"$active_text"
_nlist_print_with_ansi "$win" "$@[i]" "$text_offset" "$max_text_len"
zcurses clear "$win" eol
[ "$i" = "$highlight" ] && zcurses attr "$win" -"$active_text"
y+=1
done
if [ "$end_idx" -lt "$max_idx" ]; then
zcurses move "$win" $y "$x_offset"
zcurses clear "$win" eol
fi
zcurses attr "$win" white/black
# vim: set filetype=zsh:
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment