Skip to content

Commit

Permalink
Refactor string escaping code (#114)
Browse files Browse the repository at this point in the history
Fix #111
  • Loading branch information
denisidoro authored Oct 5, 2019
1 parent 13ddc7c commit 6129d9e
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 125 deletions.
2 changes: 1 addition & 1 deletion navi
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ source "${SCRIPT_DIR}/src/main.sh"
##? full docs
##? Please refer to the README at https://github.com/denisidoro/navi

VERSION="0.11.1"
VERSION="0.12.0"
NAVI_ENV="${NAVI_ENV:-prod}"

opts::eval "$@"
Expand Down
35 changes: 22 additions & 13 deletions src/arg.sh
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
#!/usr/bin/env bash

ARG_REGEX_WITHOUT_BRACKETS="[a-zA-Z_]+([- ]?\w+)*"
ARG_REGEX="<${ARG_REGEX_WITHOUT_BRACKETS}>"
ARG_DELIMITER="\f"
ARG_DELIMITER_2="\v"
ARG_DELIMITER_3="\r"
ARG_REGEX="<[a-zA-Z_]+([- ]?\w+)*>"

arg::dict() {
local -r input="$(cat | sed 's/\\n/\\f/g')"
local -r input="$(cat)"

local -r fn="$(echo "$input" | awk -F'---' '{print $1}')"
local -r opts="$(echo "$input" | awk -F'---' '{print $2}')"

dict::new fn "$fn" opts "$opts"
}

arg::escape() {
echo "$*" \
| tr '-' '_' \
| tr ' ' '_'
}

arg::interpolate() {
local -r arg="$1"
local -r value="$2"
Expand All @@ -40,12 +42,19 @@ arg::deserialize() {

arg="${arg:1:${#arg}-2}"
echo "$arg" \
| tr "${ARG_DELIMITER}" " " \
| tr "${ARG_DELIMITER_2}" "'" \
| tr "${ARG_DELIMITER_3}" '"'
| tr "${ESCAPE_CHAR}" " " \
| tr "${ESCAPE_CHAR_2}" "'" \
| tr "${ESCAPE_CHAR_3}" '"'
}

arg::serialize_code() {
printf "tr ' ' '${ESCAPE_CHAR}'"
printf " | "
printf "tr \"'\" '${ESCAPE_CHAR_2}'"
printf " | "
printf "tr '\"' '${ESCAPE_CHAR_3}'"
}

# TODO: separation of concerns
arg::pick() {
local -r arg="$1"
local -r cheat="$2"
Expand All @@ -54,7 +63,7 @@ arg::pick() {
local -r length="$(echo "$prefix" | str::length)"
local -r arg_dict="$(echo "$cheat" | grep "$prefix" | str::sub $((length + 1)) | arg::dict)"

local -r fn="$(dict::get "$arg_dict" fn | sed 's/\\f/\\n/g')"
local -r fn="$(dict::get "$arg_dict" fn)"
local -r args_str="$(dict::get "$arg_dict" opts)"
local arg_name=""

Expand All @@ -70,10 +79,10 @@ arg::pick() {
if [ -n "$fn" ]; then
local suggestions="$(eval "$fn" 2>/dev/null)"
if [ -n "$suggestions" ]; then
echo "$suggestions" | ui::pick --prompt "$arg: " --header-lines "${headers:-0}" | str::column "${column:-}"
echo "$suggestions" | ui::fzf --prompt "$arg: " --header-lines "${headers:-0}" | str::column "${column:-}"
fi
elif ${NAVI_USE_FZF_ALL_INPUTS:-false}; then
echo "" | ui::pick --prompt "$arg: " --print-query --no-select-1 --height 1
echo "" | ui::fzf --prompt "$arg: " --print-query --no-select-1 --height 1
else
printf "\033[0;36m${arg}:\033[0;0m " > /dev/tty
read -r value
Expand Down
37 changes: 17 additions & 20 deletions src/cheat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@ cheat::find() {
done
}

cheat::_join_multiline_using_sed() {
tr '\n' '\f' \
| sed -E 's/\\\f *//g' \
| tr '\f' '\n'
cheat::export_cache() {
if [ -z "${NAVI_CACHE:-}" ]; then
export NAVI_CACHE="$*"
fi
}

cheat::_join_multiline() {
if ${NAVI_USE_PERL:-false}; then
perl -0pe 's/\\\n *//g' \
|| cheat::_join_multiline_using_sed
cheat::join_lines() {
if command_exists perl; then
perl -0pe 's/\\\n *//g'
else
cheat::_join_multiline_using_sed
tr '\n' "$ESCAPE_CHAR" \
| sed -E 's/\\'$(printf "$ESCAPE_CHAR")' *//g' \
| tr "$ESCAPE_CHAR" '\n'
fi
}

cheat::read_all() {
for cheat in $(cheat::find); do
echo
cat "$cheat" | cheat::_join_multiline
cat "$cheat"
echo
done
}
Expand All @@ -34,16 +35,13 @@ cheat::memoized_read_all() {
echo "$NAVI_CACHE"
return
fi
if command_exists perl; then
export NAVI_USE_PERL=true
else
export NAVI_USE_PERL=false
fi

local -r cheats="$(cheat::read_all)"
echo "$cheats"
echo "$cheats" \
| cheat::join_lines
}

cheat::pretty() {
cheat::prettify() {
awk 'function color(c,s) {
printf("\033[%dm%s\033[0m",30+c,s)
}
Expand All @@ -54,7 +52,7 @@ cheat::pretty() {
NF { print color(7, $0) color(60, tags); next }'
}

cheat::_until_percentage() {
cheat::until_percentage() {
awk 'BEGIN { count=0; }
/^%/ { if (count >= 1) exit;
Expand All @@ -70,6 +68,5 @@ cheat::from_selection() {

echo "$cheats" \
| grep "% ${tags}" -A99999 \
| cheat::_until_percentage \
|| (echoerr "No valid cheatsheet!"; exit 67)
| cheat::until_percentage
}
41 changes: 41 additions & 0 deletions src/cmd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash

cmd::loop() {
local -r cmd="$1"
local -r cheat="$2"

local arg escaped_arg value escaped_cmd

arg="$(echo "$cmd" | arg::next)"
if [ -z "$arg" ]; then
dict::new cmd "$cmd"
return
fi

escaped_arg="$(arg::escape "$arg")"

escaped_cmd="$(echo "$cmd" | sed "s|<${arg}>|<${escaped_arg}>|g")"
arg="$escaped_arg"

local -r values="$(dict::get "$OPTIONS" values)"
value="$(echo "$values" | coll::get $i)"
[ -z "$value" ] && value="$(arg::pick "$arg" "$cheat")"

dict::new \
cmd "${escaped_cmd:-}" \
value "$value" \
arg "$arg"
}

cmd::finish() {
local -r cmd="$1"

local -r unresolved_arg="$(echo "$cmd" | arg::next)"

local -r print="$(dict::get "$OPTIONS" print)"
if $print || [ -n "$unresolved_arg" ]; then
echo "$cmd"
else
eval "$cmd"
fi
}
4 changes: 2 additions & 2 deletions src/coll.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ coll::remove() {
done
}

coll::_without_empty_line() {
coll::without_empty_line() {
local -r input="$(cat)"
local -r words="$(echo "$input" | wc -w | xargs)"
if [[ $words > 0 ]]; then
Expand All @@ -47,7 +47,7 @@ coll::_without_empty_line() {
}

coll::add() {
cat | coll::_without_empty_line
cat | coll::without_empty_line
for x in "$@"; do
echo "$x"
done
Expand Down
27 changes: 10 additions & 17 deletions src/dict.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
# values with non-trivial whitespaces (newlines, subsequent spaces, etc)
# aren't handled very well

DICT_DELIMITER='\f'

dict::new() {
if [ $# = 0 ]; then
echo ""
else
echo "" | dict::assoc "$@" | sed '/^$/d'
echo "" | dict::assoc "$@" | str::remove_empty_lines
fi
}

Expand All @@ -23,29 +21,29 @@ dict::dissoc() {
grep -Ev "^[\s]*${key}[^:]*:"
}

dict::_escape_value() {
tr '\n' "$DICT_DELIMITER" | sed "s/\\n/${DICT_DELIMITER}/g"
dict::escape_value() {
tr '\n' "$ESCAPE_CHAR" | sed 's/\\n/'$(printf "$ESCAPE_CHAR")'/g'
}

str::_without_trailing_newline() {
str::without_trailing_newline() {
printf "%s" "$(cat)"
echo
}

dict::_unescape_value() {
tr "$DICT_DELIMITER" '\n' | str::_without_trailing_newline
dict::unescape_value() {
tr "$ESCAPE_CHAR" '\n' | str::without_trailing_newline
}

dict::assoc() {
local -r key="${1:-}"
local -r input="$(cat)"

if [ -z $key ]; then
printf "$(echo "$input" | tr '%' '\v')" | tr '\v' '%'
printf "$(echo "$input" | tr '%' "$ESCAPE_CHAR_2")" | tr "$ESCAPE_CHAR_2" '%'
return
fi

local -r value="$(echo "${2:-}" | dict::_escape_value)"
local -r value="$(echo "${2:-}" | dict::escape_value)"

shift 2
echo "$(echo "$input" | dict::dissoc "$key")${key}: ${value}\n" | dict::assoc "$@"
Expand All @@ -65,9 +63,9 @@ dict::get() {
local -r matches="$(echo "$result" | wc -l || echo 0)"

if [ $matches -gt 1 ]; then
echo "$result" | dict::_unescape_value
echo "$result" | dict::unescape_value
else
echo "$result" | sed -E "s/${prefix}//" | dict::_unescape_value
echo "$result" | sed -E "s/${prefix}//" | dict::unescape_value
fi
}

Expand All @@ -81,11 +79,6 @@ dict::values() {
| cut -c3-
}

dict::merge() {
awk -F':' '{$1=""; print $0}' \
| cut -c3-
}

dict::zipmap() {
IFS='\n'

Expand Down
Loading

0 comments on commit 6129d9e

Please sign in to comment.