]> arthur.barton.de Git - ax-zsh.git/blob - bin/axzshctl
cd463e3ded886e9f5b4ab08bed5925d00b8e9e22
[ax-zsh.git] / bin / axzshctl
1 #!/usr/bin/env zsh
2 #
3 # AX-ZSH: Alex' Modular ZSH Configuration
4 # Copyright (c) 2015-2017 Alexander Barton <alex@barton.de>
5 #
6
7 # Include "ax-common.sh", if available:
8 for dir ("$HOME/lib" "$HOME/.ax" /usr/local /opt/ax /usr); do
9         [[ -z "$ax_common_sourced" ]] || break
10         ax_common="${dir}/lib/ax/ax-common.sh"
11         [[ -r "$ax_common" ]] && source "$ax_common"
12 done
13 if [[ -z "$ax_common_sourced" ]]; then
14         function ax_msg {
15                 shift
16                 echo "$@"
17         }
18         function ax_error {
19                 ax_msg 2 "$@" >&2
20         }
21 fi
22 unset dir ax_common ax_common_sourced
23
24 function Usage {
25         echo "Usage: $NAME <command> [...]"
26         echo
27         echo "  enable"
28         echo "    Enable AX-ZSH altogether."
29         echo "  disable"
30         echo "    Disable AX-ZSH altogether."
31         echo
32         echo "  enable-plugin <name|directory> [<name|directory> [...]]"
33         echo "    Enable plugin(s)."
34         echo "  disable-plugin <name> [<name> [...]]"
35         echo "    Disable plugin(s)."
36         echo "  list-enabled"
37         echo "    List enabled plugins."
38         echo
39         echo "  reset-plugins"
40         echo "    Reset active plugins to the default set."
41         echo "  enable-default-plugins"
42         echo "    Enable all default plugins."
43         echo "  check-plugins"
44         echo "    Detect plugins which are \"useful\" on this system."
45         echo
46         echo "  set-theme <name>|-"
47         echo "    Set active theme to <name>, or to the default."
48         echo
49         echo "  upgrade"
50         echo "    Upgrade AX-ZSH installation (requires Git)."
51         echo "  update-caches"
52         echo "    Force rebuild of all cache files."
53         echo
54         exit 0
55 }
56
57 function UpdatePluginCache {
58         [[ -r "$AXZSH/cache" ]] || return 0
59
60         ax_msg - "Updating plugin cache ..."
61         rm -rf \
62                 $AXZSH/cache/zlogin.cache \
63                 $AXZSH/cache/zlogout.cache \
64                 $AXZSH/cache/zprofile.cache \
65                 $AXZSH/cache/zshrc.cache \
66                 || return 1
67         echo "Regenerating cache files ..."
68         zsh -ilc '' >/dev/null
69 }
70
71 function NormalizedPluginName {
72         if [[ "$1" =~ "^[[:alnum:]-]+/[[:alnum:]_-]+$" ]]; then
73                 echo "${1:gs/\//#}"
74         elif [[ "$1" =~ "/" ]]; then
75                 echo "${1:t}"
76         else
77                 echo "$1"
78         fi
79 }
80
81 function EnableAXZSH {
82         for f (~/.zlogin ~/.zlogout ~/.zprofile ~/.zshrc); do
83                 ln -sv "$AXZSH/ax.zsh" "$f" \
84                         || ax_error "Failed to create symbolic link for \"$f\"!"
85         done
86 }
87
88 function DisableAXZSH {
89         for f (~/.zlogin ~/.zlogout ~/.zprofile ~/.zshrc); do
90                 if [ -h "$f" ]; then
91                         rm -v "$f" || ax_msg 2 "Failed to remove \"$f\"!"
92                 elif [ -e "$f" ]; then
93                         ax_error "Error: Not removing \"$f\", it is not a symbolic link!"
94                 else
95                         ax_msg 1 "Warning: \"$f\" already does not exist. Ok."
96                 fi
97         done
98 }
99
100 function EnablePlugin {
101         local plugin=$(NormalizedPluginName "$1")
102         local dir="$AXZSH/active_plugins"
103
104         if [[ -h "$dir/$plugin" ]]; then
105                 ax_msg 1 "Plugin \"$1\" already active!"
106                 return 1
107         fi
108
109         if [[ "$1" =~ "^[[:alnum:]-]+/[[:alnum:]_-]+$" ]]; then
110                 # GitHub plugin
111                 mkdir -p "$AXZSH/repos"
112                 if [[ ! -e "$AXZSH/repos/$plugin" ]]; then
113                         ax_msg - "Cloning module from GitHub ..."
114                         git clone --depth=1 "https://github.com/$1.git" \
115                          "$AXZSH/repos/$plugin"
116                 fi
117                 # Try to enable a theme in this "foreign module", but ignore
118                 # errors: we don't know if this module provides a theme or is
119                 # a "regular" plugin ...
120                 if SetTheme "${plugin#*#}" 2>/dev/null; then
121                         ax_msg 1 "Module \"$1\" was enabled as theme \"${plugin#*#}\"."
122                         # A theme was enabled: So assume that this is a theme
123                         # and don't enable it as plugin.
124                         return 0
125                 fi
126                 echo "Module \"$1\" will be enabled as plugin ..."
127         fi
128
129         for dname (
130                 "$plugin:A"
131                 "$AXZSH_PLUGIN_D/$plugin"
132                 "$ZSH_CUSTOM/$plugin"
133                 "$AXZSH/custom_plugins/$plugin"
134                 "$AXZSH/repos/$plugin"
135                 "$AXZSH/plugins/$plugin"
136                 "$AXZSH/default_plugins/$plugin"
137                 "$AXZSH/core/$plugin"
138         ); do
139                 [[ ! -d "$dname" ]] && continue
140                 mkdir -p "$dir"
141                 (
142                         cd "$dir" || exit 9
143                         ln -sv "$dname" "$PWD"
144                 )
145                 return $?
146         done
147
148         ax_error "Plugin \"$1\" not found!"
149         return 1
150 }
151
152 function DisablePlugin {
153         local plugin=$(NormalizedPluginName "$1")
154         local dir="$AXZSH/active_plugins"
155         local r=-1
156
157         # Active theme?
158         if [[ $(readlink "$AXZSH/active_theme") = "$AXZSH/repos/$plugin/"* ]]; then
159                 rm "$AXZSH/active_theme"; r=$?
160         fi
161
162         # Active plugin?
163         if [[ -h "$dir/$plugin" ]]; then
164                 rm "$dir/$plugin"; r=$?
165         fi
166
167         if [[ $r -eq -1 ]]; then
168                 ax_msg 1 "Plugin \"$1\" not active, nothing to do?"
169                 r=1
170         fi
171
172         if [[ "$plugin" = *"#"* ]]; then
173                 # Name matches a cloned repository, try to clean up!
174                 echo "Cleaning up cloned repository ..."
175                 rm -fr "$AXZSH/repos/$plugin"
176         fi
177
178         return $r
179 }
180
181 function ListEnabledPlugins {
182         for plugin ($AXZSH/active_plugins/*(N)); do
183                 print ${plugin:t:s/#/\//}
184         done
185         return 0
186 }
187
188 function ResetPlugins {
189         local dir="$AXZSH/active_plugins"
190         local r1=0, r2=0
191
192         if [[ -e "$dir" ]]; then
193                 ax_msg - "Removing all symbolic links in $dir ..."
194                 find "$dir" -type l -print -delete; r1=$?
195         fi
196
197         ax_msg - "Removing all external repositories in \"$AXZSH/repos\" ..."
198         rm -fr "$AXZSH/repos"; r2=$?
199
200         [[ $r1 == 0 && $r2 == 0 ]] && return 0 || return 1
201 }
202
203 function EnableDefaultPlugins {
204         local dir="$AXZSH/active_plugins"
205
206         ax_msg - "Activating (linking) default plugins ..."
207         mkdir -p "$dir"
208         (
209                 cd "$dir" || exit 9
210                 ln -sfv "$AXZSH/default_plugins/"* "$PWD"
211         )
212         return $?
213 }
214
215 function SetTheme {
216         local link_name="$AXZSH/active_theme"
217
218         if [ "$1" = "-" ]; then
219                 rm -f "$link_name" || return 1
220                 ax_msg 0 "Theme settings have been reset."
221                 return 0
222         fi
223
224         if [ -r "$1" ]; then
225                 theme="$1"
226         elif [ -r "$AXZSH/custom_themes/$1.axzshtheme" ]; then
227                 theme="$AXZSH/custom_themes/$1.axzshtheme"
228         elif [ -r "$AXZSH/themes/$1.axzshtheme" ]; then
229                 theme="$AXZSH/themes/$1.axzshtheme"
230         else
231                 # Look for theme inside of installed plugins:
232                 for dname (
233                         "$AXZSH/custom_themes"
234                         "$AXZSH/custom_plugins/"*(N)
235                         "$AXZSH/repos/"*(N)
236                 ); do
237                         if [[ -r "$dname/$1.axzshtheme" ]]; then
238                                 theme="$dname/$1.axzshtheme"
239                                 break
240                         elif [[ -r "$dname/$1.zsh-theme" ]]; then
241                                 theme="$dname/$1.zsh-theme"
242                                 break
243                         fi
244                 done
245                 if [[ -z "$theme" ]]; then
246                         ax_error "Theme \"$1\" not found!"
247                         return 1
248                 fi
249         fi
250         ln -fsv "$theme" "$link_name" || return 1
251         return $?
252 }
253
254 function UpgradeAXZSH {
255         if [[ $+commands[git] -eq 0 ]]; then
256                 ax_error "The git(1) command is not available!"
257                 return 1
258         fi
259         if [[ ! -d "$AXZSH/.git" ]]; then
260                 ax_error "AX-ZSH seems not to be installed using Git. Can't upgrade!"
261                 return 1
262         fi
263
264         ax_msg - "Upgrading AX-ZSH in \"$AXZSH\" using git(1) ..."
265         ( cd "$AXZSH" && git pull --ff-only )
266 }
267
268 function UpgradeForeignPlugins {
269         if [[ $+commands[git] -eq 0 ]]; then
270                 ax_error "The git(1) command is not available!"
271                 return 1
272         fi
273
274         for dir ($AXZSH/repos/*(N)); do
275                 name=${dir:t:s/#/\//}
276                 if [ -d "$dir/.git" ]; then
277                         ax_msg - "Upgrading \"$name\" [git] ..."
278                         (
279                                 cd "$dir"
280                                 git pull --ff-only || ax_error "Pull failed!"
281                         )
282                 else
283                         ax_error "Unknown repository type!"
284                 fi
285         done
286 }
287
288 function CheckPlugins {
289         missing_plugins=()
290         invalid_plugins=()
291
292         ax_msg - "Checking plugins ..."
293         for dir ($AXZSH/plugins/*(N)); do
294                 plugin=${dir:t}
295
296                 # Test if plugin is already enabled
297                 [[ -e "$AXZSH/active_plugins/$plugin" ]] \
298                         && enabled=" (enabled)" \
299                         || unset enabled
300
301                 # Test plugin ...
302                 printf " - \"%s\"%s ... " "$plugin" "$enabled"
303                 new_plugin=""
304                 for script ($AXZSH/plugins/$plugin/$plugin.{zshrc,zprofile}); do
305                         [[ -r "$script" ]] || continue
306                         AXZSH_PLUGIN_CHECK=1 zsh -i -c "source $script"; r=$?
307                         if [[ $r -eq 0 ]]; then
308                                 new_plugin=$plugin
309                                 break
310                         fi
311                 done
312                 if [[ -n "$new_plugin" ]]; then
313                         detected_plugins+=($new_plugin)
314                         [[ -n "$enabled" ]] || missing_plugins+=($new_plugin)
315                         ax_msg 0 "OK."
316                 elif [[ $r -eq 91 ]]; then
317                         ax_msg 1 "ignored."
318                 elif [[ $r -eq 92 ]]; then
319                         ax_msg 1 "optional."
320                 else
321                         [[ -n "$enabled" ]] && invalid_plugins+=($plugin)
322                         ax_msg 2 "failed."
323                 fi
324         done
325         echo
326
327         result=0
328         if [[ -n "$missing_plugins" ]]; then
329                 ax_msg 1 "Run the following command to enable all missing plugins:"
330                 echo "$AXZSH/bin/axzshctl enable-plugin" $missing_plugins
331                 echo
332                 result=1
333         else
334                 ax_msg 0 "All detected plugins are already enabled."
335         fi
336
337         if [[ -n "$invalid_plugins" ]]; then
338                 ax_msg 1 "Run the following command to disable all failed plugins:"
339                 echo "$AXZSH/bin/axzshctl disable-plugin" $invalid_plugins
340                 result=1
341         else
342                 ax_msg 0 "No failed plugins are enabled."
343         fi
344
345         echo
346         return $result
347 }
348
349 NAME="$0:t"
350
351 [[ $# -gt 0 ]] || Usage
352
353 if [[ -z "$AXZSH" || ! -r "$AXZSH/ax.zsh" ]]; then
354         [[ -r "$HOME/.axzsh/ax.zsh" ]] && AXZSH="$HOME/.axzsh"
355         if [[ ! -r "$AXZSH/ax.zsh" ]]; then
356                 ax_error "Oops, \"AXZSH\" is not set or invalid and can't be autodetected!"
357                 exit 3
358         fi
359 fi
360
361 cmd="$1"
362 shift
363
364 case "$cmd" in
365         "enable")
366                 [[ $# -eq 0 ]] || Usage
367                 EnableAXZSH
368                 ;;
369         "disable")
370                 [[ $# -eq 0 ]] || Usage
371                 DisableAXZSH
372                 ;;
373         "enable-plugin")
374                 [[ $# -gt 0 ]] || Usage
375                 for plugin in "$@"; do
376                         EnablePlugin "$plugin"
377                 done
378                 UpdatePluginCache
379                 ;;
380         "disable-plugin")
381                 [[ $# -gt 0 ]] || Usage
382                 for plugin in "$@"; do
383                         DisablePlugin "$plugin"
384                 done
385                 UpdatePluginCache
386                 ;;
387         "list-enabled")
388                 [[ $# -eq 0 ]] || Usage
389                 ListEnabledPlugins
390                 ;;
391         "reset-plugins")
392                 [[ $# -eq 0 ]] || Usage
393                 ResetPlugins
394                 EnableDefaultPlugins
395                 UpdatePluginCache
396                 ;;
397         "enable-default-plugins")
398                 [[ $# -eq 0 ]] || Usage
399                 EnableDefaultPlugins && UpdatePluginCache
400                 ;;
401         "check-plugins")
402                 [[ $# -eq 0 ]] || Usage
403                 CheckPlugins
404                 ;;
405         "set-theme")
406                 [[ $# -eq 1 ]] || Usage
407                 SetTheme "$1"
408                 ;;
409         "upgrade")
410                 [[ $# -eq 0 ]] || Usage
411                 UpgradeAXZSH
412                 UpgradeForeignPlugins
413                 UpdatePluginCache
414                 ;;
415         "update-caches")
416                 [[ $# -eq 0 ]] || Usage
417                 UpdatePluginCache
418                 ;;
419         "--help")
420                 Usage
421                 ;;
422         *)
423                 ax_error "Invalid command \"$cmd\"!"
424                 ax_error "Try \"$0 --help\" for more information."
425                 exit 2
426 esac