Improve performance with single-pass awk implementation

This commit is contained in:
Jorge Morante 2016-05-08 17:06:02 +02:00
parent 6c297dee65
commit e8c1e59e29
10 changed files with 239 additions and 107 deletions

View File

@ -1,7 +1,9 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/utils.sh
# TODO empty patterns are invalid
function check_pattern() {
echo "beep beep" | grep -e "$1" 2> /dev/null
@ -12,15 +14,31 @@ function check_pattern() {
fi
}
HAS_GAWK=$(which gawk &> /dev/null && echo $(($? == 0)))
function supports_intervals_in_awk() {
echo "wtfwtfwtf" | __awk__ "/(wtf){3}/ { print \"wtf\" }" | grep -c wtf
}
source "$CURRENT_DIR/utils.sh"
PATTERNS_LIST=(
"((^|^\.|[[:space:]]|[[:space:]]\.|[[:space:]]\.\.|^\.\.)[[:alnum:]~_-]*/[][[:alnum:]_.#$%&+=/@-]*)"
"([[:digit:]]{4,})"
"([0-9a-f]{7}|[0-9a-f]{40})"
"((https?://|git@|git://|ssh://|ftp://|file:///)[[:alnum:]?=%/_.:,;~@!#$&()*+-]*)"
"([[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3})"
)
if [[ supports_intervals_in_awk == "1" ]]; then
PATTERNS_LIST=(
"((^|^\.|[[:space:]]|[[:space:]]\.|[[:space:]]\.\.|^\.\.)[[:alnum:]~_-]*/[][[:alnum:]_.#$%&+=/@-]*)"
"([[:digit:]]{4,})"
"([0-9a-f]{7}|[0-9a-f]{40})"
"((https?://|git@|git://|ssh://|ftp://|file:///)[[:alnum:]?=%/_.:,;~@!#$&()*+-]*)"
"([[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3})"
)
else
PATTERNS_LIST=(
"((^|^\.|[[:space:]]|[[:space:]]\.|[[:space:]]\.\.|^\.\.)[[:alnum:]~_-]*/[][[:alnum:]_.#$%&+=/@-]*)"
"([[:digit:]][[:digit:]][[:digit:]][[:digit:]]([[:digit:]])*)"
"([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]|[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])"
"((https?://|git@|git://|ssh://|ftp://|file:///)[[:alnum:]?=%/_.:,;~@!#$&()*+-]*)"
"([[:digit:]][[:digit:]]?[[:digit:]]?\.[[:digit:]][[:digit:]]?[[:digit:]]?\.[[:digit:]][[:digit:]]?[[:digit:]]?\.[[:digit:]][[:digit:]]?[[:digit:]]?)"
)
fi
IFS=$'\n'
USER_DEFINED_PATTERNS=($(tmux show-options -g | grep ^@fingers-pattern | sed 's/^@fingers-pattern-[0-9] "\(.*\)"$/(\1)/'))

View File

@ -5,6 +5,10 @@
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
function current_ms() {
echo $(($(date +%s%N)/1000000))
}
function log() {
echo "$1" >> "$CURRENT_DIR/../fingers.log"
}

View File

@ -1,11 +1,13 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/config.sh
source $CURRENT_DIR/actions.sh
source $CURRENT_DIR/hints.sh
source $CURRENT_DIR/utils.sh
LOG_PATH=$CURRENT_DIR/../fingers.log
FINGERS_COPY_COMMAND=$(tmux show-option -gqv @fingers-copy-command)
current_pane_id=$1
@ -14,15 +16,6 @@ tmp_path=$3
BACKSPACE=$'\177'
function clear_screen() {
clear
tmux clearhist -t "$fingers_pane_id"
}
function has_capitals() {
echo "$1" | grep -c "[A-Z]"
}
function is_pane_zoomed() {
local pane_id=$1
@ -37,12 +30,6 @@ function zoom_pane() {
tmux resize-pane -Z -t "$pane_id"
}
clear_screen
print_hints
pane_was_zoomed=$(is_pane_zoomed "$current_pane_id")
tmux swap-pane -s "$current_pane_id" -t "$fingers_pane_id"
[[ $pane_was_zoomed == "1" ]] && zoom_pane "$fingers_pane_id"
function handle_exit() {
tmux swap-pane -s "$current_pane_id" -t "$fingers_pane_id"
[[ $pane_was_zoomed == "1" ]] && zoom_pane "$current_pane_id"
@ -68,20 +55,6 @@ function copy_result() {
fi
}
function sanitize_input() {
local input=$(echo "$(str_to_ascii "$1")" | sed -r "s/ 27 91 [0-9]{2}//")
local sanitized=''
OLDIFS=$IFS
IFS=' '
for char_code in $input; do
sanitized="${sanitized}$(chr "$char_code")"
done
IFS=$OLDIFS
echo "$sanitized"
}
function is_valid_input() {
local input=$1
local is_valid=1
@ -98,10 +71,18 @@ function is_valid_input() {
echo $is_valid
}
function hide_cursor() {
echo -n $(tput civis)
}
trap "handle_exit" EXIT
input=''
pane_was_zoomed=$(is_pane_zoomed "$current_pane_id")
show_hints_and_swap $current_pane_id $fingers_pane_id
[[ $pane_was_zoomed == "1" ]] && zoom_pane "$fingers_pane_id"
hide_cursor
input=''
while read -rsn1 char; do
# Escape sequence, flush input
if [[ "$char" == $'\x1b' ]]; then

149
scripts/hinter.awk Normal file
View File

@ -0,0 +1,149 @@
BEGIN {
n_matches = 0;
line_pos = 0;
col_pos = 0;
HINTS[0] = "p"
HINTS[1] = "o"
HINTS[2] = "i"
HINTS[3] = "u"
HINTS[4] = "l"
HINTS[5] = "k"
HINTS[6] = "j"
HINTS[7] = "t"
HINTS[8] = "r"
HINTS[9] = "e"
HINTS[10] = "wj"
HINTS[11] = "wt"
HINTS[12] = "wr"
HINTS[13] = "we"
HINTS[14] = "ww"
HINTS[15] = "wq"
HINTS[16] = "wf"
HINTS[17] = "wd"
HINTS[18] = "ws"
HINTS[19] = "wa"
HINTS[20] = "qp"
HINTS[21] = "qo"
HINTS[22] = "qi"
HINTS[23] = "qu"
HINTS[24] = "ql"
HINTS[25] = "qk"
HINTS[26] = "qj"
HINTS[27] = "qt"
HINTS[28] = "qr"
HINTS[29] = "qe"
HINTS[30] = "qw"
HINTS[31] = "qq"
HINTS[32] = "qf"
HINTS[33] = "qd"
HINTS[34] = "qs"
HINTS[35] = "qa"
HINTS[36] = "fp"
HINTS[37] = "fo"
HINTS[38] = "fi"
HINTS[39] = "fu"
HINTS[40] = "fl"
HINTS[41] = "fk"
HINTS[42] = "fj"
HINTS[43] = "ft"
HINTS[44] = "fr"
HINTS[45] = "fe"
HINTS[46] = "fw"
HINTS[47] = "fq"
HINTS[48] = "ff"
HINTS[49] = "fd"
HINTS[50] = "fs"
HINTS[51] = "fa"
HINTS[52] = "dp"
HINTS[53] = "do"
HINTS[54] = "di"
HINTS[55] = "du"
HINTS[56] = "dl"
HINTS[57] = "dk"
HINTS[58] = "dj"
HINTS[59] = "dt"
HINTS[60] = "dr"
HINTS[61] = "de"
HINTS[62] = "dw"
HINTS[63] = "dq"
HINTS[64] = "df"
HINTS[65] = "dd"
HINTS[66] = "ds"
HINTS[67] = "da"
HINTS[68] = "sp"
HINTS[69] = "so"
HINTS[70] = "si"
HINTS[71] = "su"
HINTS[72] = "sl"
HINTS[73] = "sk"
HINTS[74] = "sj"
HINTS[75] = "st"
HINTS[76] = "sr"
HINTS[77] = "se"
HINTS[78] = "sw"
HINTS[79] = "sq"
HINTS[80] = "sf"
HINTS[81] = "sd"
HINTS[82] = "ss"
HINTS[83] = "sa"
HINTS[84] = "ap"
HINTS[85] = "ao"
HINTS[86] = "ai"
HINTS[87] = "au"
HINTS[88] = "al"
HINTS[89] = "ak"
HINTS[90] = "aj"
HINTS[91] = "at"
HINTS[92] = "ar"
HINTS[93] = "ae"
HINTS[94] = "aw"
HINTS[95] = "aq"
HINTS[96] = "af"
HINTS[97] = "ad"
HINTS[98] = "as"
HINTS[99] = "aa"
finger_patterns = ENVIRON["FINGER_PATTERNS"];
hint_format = "\033[1;33m[%s]\033[0m"
highlight_format = "\033[1;33m%s\033[0m "
printf "%s\n", finger_patterns | "cat 1>&4"
}
{
line = $0;
pos = 0;
col_pos = 0;
col_pos_correction = 0;
output_line = line;
while (match(line, finger_patterns)) {
n_matches += 1;
hint = HINTS[n_matches - 1]
pos += RSTART;
col_pos = pos;
line_match = substr(line, RSTART, RLENGTH);
col_pos = col_pos + col_pos_correction
line_pos = NR;
pre_match = substr(output_line, 0, col_pos - 1);
hint_match = sprintf(highlight_format hint_format, line_match, hint);
post_match = substr(output_line, col_pos + RLENGTH, length(line) - 1);
output_line = pre_match hint_match post_match;
line = post_match;
col_pos_correction += (length(sprintf(highlight_format, line_match)) - 1 + length(sprintf(hint_format, hint)) - 1) + 1;
printf hint ":" line_match "\n" | "cat 1>&3"
}
printf "\n%s", output_line
}

View File

@ -1,68 +1,26 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source $CURRENT_DIR/utils.sh
MATCH_PARSER="\([0-9]*\):\(.*\)"
match_lookup_table=$(fingers_tmp)
HINTS=(p o i u l k j t r e wj wt wr we ww wq wf wd ws wa qp qo qi qu ql qk qj qt qr qe qw qq qf qd qs qa fp fo fi fu fl fk fj ft fr fe fw fq ff fd fs fa dp do di du dl dk dj dt dr de dw dq df dd ds da sp so si su sl sk sj st sr se sw sq sf sd ss sa ap ao ai au al ak aj at ar ae aw aq af ad as aa)
match_lookup_table=''
declare -A match_lookup_table
function get_hint() {
echo "${HINTS[$1]}"
}
function highlight() {
printf "\033[1;33m%s\033[0m" "$1"
function clear_screen() {
local fingers_pane_id=$1
clear
tmux clearhist -t $fingers_pane_id
}
function lookup_match() {
local input=$1
echo ${match_lookup_table[$input]}
echo "$(cat $match_lookup_table | grep "^$input:" | sed "s/^$input://")"
}
lines=''
OLDIFS=$IFS
IFS=
while read -r line
do
lines+="$line\n"
done < /dev/stdin
IFS=$OLDIFS
# POSIX grep does linenumber on every line with both -o and -n flags set
normalize_grep_output='
BEGIN {
previous_line_no = 0;
}
{
if ( $0 ~ /^[0-9]+:/ ) {
split($0, split_at_colon, ":")
previous_line_no = split_at_colon[1]
print $0
} else {
printf "%d:%s\n", previous_line_no, $0
}
}
'
matches=$(echo -e $lines | (grep -oniE "$PATTERNS" 2> /dev/null) | awk $normalize_grep_output | sort -u)
output="$lines"
i=0
OLDIFS=$IFS
IFS=$(echo -en "\n\b") # wtf bash?
for match in $matches ; do
hint=$(get_hint $i)
linenumber=$(echo $match | sed "s!${MATCH_PARSER//!\\!}!\1!")
text=$(echo $match | sed "s!${MATCH_PARSER//!\\!}!\2!")
output=$(echo -ne "$output" | sed "${linenumber}s!${text//!/\\!}!$(highlight ${text//!/\\!}) $(highlight "[${hint//!/\\!}]")!g")
match_lookup_table[$hint]=$text
i=$((i + 1))
done
IFS=$OLDIFS
function print_hints() {
echo -ne "$output"
function show_hints_and_swap() {
current_pane_id=$1
fingers_pane_id=$2
tmux swap-pane -s "$current_pane_id" -t "$fingers_pane_id"
clear_screen "$fingers_pane_id"
cat | FINGER_PATTERNS=$PATTERNS __awk__ -f $CURRENT_DIR/hinter.awk 3> $match_lookup_table 4>> $CURRENT_DIR/../fingers.log
cat $match_lookup_table >> $CURRENT_DIR/../fingers.log
}

View File

@ -33,8 +33,7 @@ function capture_pane() {
function prompt_fingers_for_pane() {
local current_pane_id=$1
local fingers_pane_id=$(init_fingers_pane)
local tmp_path=$(mktemp "${TMPDIR:-/tmp}/tmux-fingers.XXXXXXXX")
chmod 600 "$tmp_path"
local tmp_path=$(fingers_tmp)
wait

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
#TODO split all this crap in lib/ folder
function array_join() {
local IFS="$1"; shift; echo "$*";
@ -80,3 +80,17 @@ function pane_exec() {
tmux send-keys -t $pane_id " $pane_command"
tmux send-keys -t $pane_id Enter
}
function fingers_tmp() {
local tmp_path=$(mktemp "${TMPDIR:-/tmp}/tmux-fingers.XXXXXXXX")
chmod 600 "$tmp_path"
echo "$tmp_path"
}
function __awk__() {
if hash gawk 2>/dev/null; then
gawk "$@"
else
awk "$@"
fi
}

View File

@ -1,18 +1,26 @@
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1 scope host lo
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1 scope host
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 78:31:c1:d5:40:ce brd ff:ff:ff:ff:ff:ff
inet 192.168.1.39 brd 192.168.1.255 scope global dynamic wlp3s0
valid_lft 33919sec preferred_lft 33919sec
inet6 fe80::7a31:c1ff:fed5:40ce scope link tentative dadfailed
inet 192.168.1.33/24 brd 192.168.1.255 scope global dynamic wlp3s0
valid_lft 40162sec preferred_lft 40162sec
inet6 fe80::7a31:c1ff:fed5:40ce/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
3: vboxnet0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
3: br0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 46:06:f6:15:ea:fb brd ff:ff:ff:ff:ff:ff
inet 10.0.3.1/24 brd 10.0.3.255 scope global br0
valid_lft forever preferred_lft forever
4: vboxnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 0a:00:27:00:00:00 brd ff:ff:ff:ff:ff:ff
inet 10.0.1.1 brd 10.0.1.255 scope global vboxnet0
inet 10.0.1.1/24 brd 10.0.1.255 scope global vboxnet0
valid_lft forever preferred_lft forever
inet6 fe80::800:27ff:fe00:0 scope link
inet6 fe80::800:27ff:fe00:0/64 scope link
valid_lft forever preferred_lft forever
5: vboxnet1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0a:00:27:00:00:01 brd ff:ff:ff:ff:ff:ff
6: vboxnet2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 0a:00:27:00:00:02 brd ff:ff:ff:ff:ff:ff

View File

@ -31,7 +31,7 @@ proc init_pane {} {
proc invoke_fingers {} {
tmux_send "F";
sleep 0.5;
sleep 1.0;
}
proc echo_yanked {} {
@ -39,6 +39,7 @@ proc echo_yanked {} {
send "echo yanked text is ";
tmux_send "]";
send "\r";
sleep 0.5;
}
proc exit_ok {} {

View File

@ -12,11 +12,11 @@ sleep 0.5;
init_pane
exec "cat ./test/fixtures/ip-output";
invoke_fingers;
send "t";
send "r";
echo_yanked;
expect {
"yanked text is 10.0.1.255" { exit_ok }
"yanked text is 192.168.1.33" { exit_ok }
timeout { exit_fail }
}