You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mblaze/contrib/mvi

324 lines
5.9 KiB
Bash

#!/bin/sh
clear_buf() {
while
tput cup "${i:-0}" 0 && tput el \
&& [ "$(( i+=1 ))" -le "$1" ]
do :; done
}
term_init() {
cols=$(tput cols)
rows=$(tput lines)
start_headers=$(( rows / 4 ))
start_body=$(( start_headers + 1 ))
end_body=$(( rows - start_body - 2 ))
term_clear_headers=$(clear_buf "$start_headers"; tput cup 0 0)
term_clear_body=$(tput cup "$(( start_body ))" 0; tput ed)
term_move_status=$(tput cup "$(( rows ))" $(( cols - 5 )); tput el)
term_move_cmd=$(tput cup "$(( rows ))" 0)
term_init_cmd=$(tput cnorm; tput el)
term_done_cmd=$(tput civis)
}
statusline() {
printf "%s%s" "$term_move_status" "$@"
}
cmdline() {
printf "%s%s:" "$term_move_cmd" "$term_init_cmd"
stty "$stty_default"
tput cnorm
read -r cmd
stty -echo -icanon
case "$cmd" in
"|"*) ;;
"!"*)
tput sgr0 && tput rmcup # restore to content before mvi
eval "${cmd#!*}"
# wait for enter, and delete message
tput sc; printf "[enter to continue]" && read -r d; tput rc; tput el
tput smcup # save new content
;;
q) close ;;
esac
printf "%s" "$term_done_cmd"
update_body=1
draw
}
update() {
buf_row=1
[ "$buf_col" -le 0 ] && buf_col=1
mshow 2>/dev/null | mcolor \
| cut -c "$(( buf_col ))-$(( cols + buf_col - 1 ))" \
| trunc_lines >"$buf_path"
buf_len=$(wc -l <"$buf_path")
: $(( buf_len = buf_len + 1 ))
: $(( buf_end = buf_len - end_body ))
update_body=1
}
trunc_lines() {
t=$(tput el)
while read -r l; do
printf "%s%s\n" "$l" "$t"
done
}
headers() {
printf "%s" "$term_clear_headers"
: $(( x = hdr_row - (start_headers / 2) ))
: $(( y = hdr_row + (start_headers / 2) ))
[ "$x" -gt 0 ] && x="+$x"
[ "$y" -gt 0 ] && y="+$y"
COLUMNS=$cols mscan ".$x:.$y" 2>/dev/null \
| awk '
function fg(c, s) { return sprintf("\033[38;5;%03dm%s\033[0m", c, s) }
function so(s) { return sprintf("\033[1m%s\033[0m", s) }
/^>/ { print so(fg(119, $0)); next }
/^ *\\_/ { print fg(242, $0); next }
{ print }
'
}
body() {
[ "$buf_row" -gt "$buf_end" ] && buf_row=$buf_end
[ "$buf_row" -lt 1 ] && buf_row=1
: $(( x = buf_row ))
: $(( y = end_body + buf_row ))
[ "${update_body}" = "${x},${y}p" ] && return
printf "%s" "$term_clear_body"
update_body="${x},${y}p"
sed -n "$update_body" "$buf_path"
[ "$buf_len" -gt "$end_body" ] \
&& statusline "$(( 100 * y / buf_len ))%"
}
draw() {
headers
body
}
init() {
tput smcup # save position
tput civis # cursor invisible
term_init
stty_default=$(stty -g)
stty -echo -icanon
update
draw
}
close() {
tput sgr0 # reset char attributes
tput cnorm # normal cursor
tput rmcup # restore position
stty "$stty_default" # restore stty settings
[ -e "$buf_path" ] && rm "$buf_path"
exit "${1-0}"
}
motion() {
[ -z "$mv" ] || [ -z "$in_new" ] && return
mseq -C "$in_new";
mv=
in_new=
}
readchar() {
c=$(dd bs=1 count=1 2>/dev/null)
printf '%d' "'$c"
}
in_read() {
set -- $in_buf
[ "$#" -eq 0 ] && in_key=$(readchar) && return
in_key=$1; shift; in_buf=$@
}
in_back() {
set -- "$in_key" $in_buf
in_buf=$@
}
in_prefix() {
n=
while [ "$in_key" -le 57 ] && [ "$in_key" -ge 48 ]; do
: $(( n = n * 10 + $(printf \\$(printf "%03o" "$in_key")) ))
in_read
done
[ "$1" -eq "1" ] && in_cnt1=${n:-0} || in_cnt2=${n:-0}
}
in_motionln() {
cnt=$(( (in_cnt1 < 1 ? 1 : in_cnt1) * (in_cnt2 < 1 ? 1 : in_cnt2) ))
mv=
case "$in_key" in
# return + j
0|43|106) mv=".:.+$cnt"; in_new=".+$cnt" ;;
# - k
45|107) mv=".-$cnt:."; in_new=".-$cnt" ;;
# G
71)
[ "$in_cnt1" -eq 0 ] && [ "$in_cnt2" -eq 0 ] \
&& in_new="\$" \
&& mv="$in_cur:$in_new" \
&& return
in_new="$cnt"
[ "$cnt" -gt "$in_cur" ] \
&& mv="$in_cur:$in_new" \
|| mv="$in_new:$in_cur"
;;
# P [
80|91)
in_new=$in_cur
while [ $(( cnt-=1 )) -ge 0 ]; do
j=$(mscan -n "$in_new=" | head -n1)
mv="$j:$in_new $mv"
in_new=$(( j - 1 ))
done
: $(( in_new+=1 ))
# XXX: should [ ] move one more???
#[ "$in_cur" -eq "$in_new" ] && : $(( in_new-=1 ))
#true
;;
# N ]
78|93)
# mv=".:$(mseq ".=" | tail -n1 | mscan -n)"
in_new=$in_cur
while [ $(( cnt-=1 )) -ge 0 ]; do
j=$(mscan -n "$in_new=" | tail -n1)
mv="$mv $in_new:$j"
in_new=$(( j + 1 ))
done
: $(( in_new-=1 ))
#[ "$in_cur" -eq "$in_new" ] && : $(( in_new+=1 ))
#true
;;
# {
123) in_new=$(mscan -n ".^" | head -n1); mv="$in_new:." ;;
# }
125) in_new=$(mscan -n "._" | tail -n1); mv=".:$in_new" ;;
*) false ;;
esac
}
in_action() {
c=$in_key
in_read
in_prefix 2
in_motionln || { [ "$in_key" -eq "$c" ] && mv="."; }
[ -z "$mv" ] && return 1
case "$c" in
# d
100)
mflag -S "$mv" >/dev/null
mseq -f : | mseq -S
motion
headers
;;
# u
117)
mflag -s "$mv" >/dev/null
mseq -f : | mseq -S
motion
headers
;;
esac
}
in_esc() {
stty min 0 time 1
c=$(readchar)
rv=0
case "$c" in
91)
c=$(readchar)
# page up/down
[ "$c" -eq 53 ] && : $(( buf_row-=end_body ))
[ "$c" -eq 54 ] && : $(( buf_row+=end_body ))
c=$(readchar)
body
;;
*) c=$(readchar); c=$(readchar); rv=1 ;;
esac
stty min 1 time 0
return $rv
}
trap 'close 130;' INT TERM
buf_path=$(mktemp /tmp/.mcurse_body.XXXXXX)
buf_col=1
buf_row=1
buf_len=
buf_end=
hdr_row=0
init
while :; do
in_buf=
in_key=
in_cnt1=0
in_cnt2=0
in_cur=$(mscan -n .)
in_new=
printf "%s" "$term_move_cmd"
in_read
[ "$in_key" -eq 27 ] \
&& in_esc \
&& continue
in_prefix 1
in_motionln \
&& motion \
&& update \
&& draw \
&& continue
case "$in_key" in
# d u
100|117) in_action ;;
# D
68) in_key="100"; in_back; in_action ;; # input buffer to dd
# U
85) in_key="117"; in_back; in_action ;; # input buffer to uu
# e
101) ${EDITOR=ed} $(mseq .) && update && draw ;;
# v
118) ${VISUAL=vi} $(mseq .) && update && draw ;;
# L | C-l
76|12) tput clear && term_init && update && draw ;;
# :
58) cmdline ;;
# q
113) close ;;
# c
99) hdr_row=0 && headers ;;
# J
74) : $(( buf_row+=(in_cnt1 < 1 ? 1 : in_cnt1) )) && body ;;
# K
75) : $(( buf_row-=(in_cnt1 < 1 ? 1 : in_cnt1) )) && body ;;
esac
done