-
Notifications
You must be signed in to change notification settings - Fork 0
/
hori
executable file
·147 lines (127 loc) · 3.64 KB
/
hori
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/bin/sh
# shellcheck shell=dash disable=SC1090
yell() { echo "hori: $*" 1>&2 ; }
# -- templates
# replace $2 with $3 in string $1
replace() { echo "${1%%"$2"*}${3}${1#*"$2"}" ; }
# extract shortest substring from string $1 between $2 and $3
extract() { local s=${1%%"$3"*} ; echo "${s##*"$2"}" ; }
import() { # recursively import config into pathed variables
local varset=$1 prefix=$2
[ "$varset" = "conf" ] || prefix=${prefix}_${1##*\/}
prefix="$(echo "$prefix" | sed 's/\//_/g')" # FIXME get rid of sed maybe
while read -r line ; do
if [ "${line#"import "}" != "$line" ]; then
import "${line#"import "}" "$prefix"
elif [ "${line#[[:alnum:]]*"="}" != "$line" ]; then # nb: only alnum in assignments
eval "_${prefix}_${line}"
fi
done < "$REPO/hori/$varset"
}
template() { # render <1:file> with the current context
local expr old_expr key val
while IFS= read -r line; do
while [ "${line#*"{% "[!" "]*" %}"*}" != "$line" ]; do
expr="$(extract "$line" "{% " " %}")"
old_expr=$expr
while [ "${expr#*"{"[!\{\}]*"}"*}" != "$expr" ]; do
key="$(extract "$expr" "{" "}")"
val="$(eval "echo \$__$key")"
[ -z "$val" ] && { yell "'$key' is undefined, aborting" ; exit 1 ; }
expr="$(replace "$expr" "{$key}" "$val")"
done
line="$(replace "$line" "{% $old_expr %}" "$expr")"
done
echo "$line"
done < "$1"
}
# -- actions
launch() { # kill existing <1:process> and run <@:command>
pkill -x "$1" ; sleep 0.2 ; "$@"
}
render() { # render <1:file(rel)> and return its path
local source=$REPO/$PACK/$1
local target=$HORI/$PACK/$1
mkdir -p "${target%/*}"
template "$source" > "$target"
echo "$target"
}
link() { # create symlink from <1:source(rel)> to <2:target(abs)>
local source target=$2
[ "${1#*"$HORI"*}" != "$1" ] && source=$1 || source=$REPO/$PACK/$1
mkdir -p "${target%/*}"
ln -sfn "$source" "$target"
}
unlink() { # remove <1:symlink(abs)>
[ -h "$1" ] && rm "$1"
}
# -- hook
list() {
case $* in
-i|--installed) packages="$HORI/*" ;;
-a|--available) packages="$REPO/*" ;;
*) packages="$*" ;;
esac
for p in $packages; do
p="${p##*/}"
if [ -f "$REPO/$p/hori.sh" ]; then
echo "$p"
# else
# TODO why is this here and not in hook?
# yell "{$p} is not a valid package"
fi
done
}
edit() {
local c
c="$(for p in $(list -a); do
find "$REPO/$p/" | while read -r line; do
echo "${line##*"$REPO/"}"
done
done | fzf)"
if [ -n "$c" ]; then
if [ -f "$HORI/$c" ]; then
echo "$REPO/$c" | entr -np hori add "${c%%/*}" &
pid=$!
fi
$EDITOR "$REPO/$c"
if [ -n "$pid" ]; then kill "$pid"; else exit 0; fi
fi
}
hook() { # execute <1:hook> in <@:packages>
local h=$1 ; shift
list "$@" | while read -r p; do
PACK=$p ; (
. "$REPO/$p/hori.sh"
if type "$h" >/dev/null 2>&1; then
case $h in
add) mkdir -p "$HORI/$p" ;;
esac
yell "<$h> in {$p}"
"$h"
case $h in
del) [ -d "${HORI:?}/$p" ] && rm -r "${HORI:?}/$p" ;;
esac
else
yell "no <$h> hook in {$p} package"
fi
) ; unset PACK
done
}
# -- main
HORI="$HOME/.config/hori"
REPO="$(cat "$HORI/.repo" 2>/dev/null || dirname "$(find ~ -type f -name "repository.hori" -print -quit 2>/dev/null)")"
if [ -f "$REPO/repository.hori" ]; then
mkdir -p "$HORI"
echo "$REPO" > "$HORI/.repo"
else
yell "could not find repo, aborting" ; exit 1
fi
import conf
cmd=$1 ; shift
case $cmd in
-h|--help|"") echo "\$ hori <hook> (<package(s)>|-i|-a)\n from $REPO" ;;
l|list) list "$@" ;;
e|edit) edit "$@" ;;
*) hook "$cmd" "$@" ;;
esac