]> arthur.barton.de Git - ax-zsh.git/blob - bin/axzshctl
axzshctl: Streamline messages on "enable-plugin"
[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                                 || ax_error "Failed to clone repository!"
117                 fi
118                 # Try to enable a theme in this "foreign module", but ignore
119                 # errors: we don't know if this module provides a theme or is
120                 # a "regular" plugin ...
121                 if SetTheme "${plugin#*#}" 2>/dev/null; then
122                         ax_msg 0 "Module \"$1\" was enabled as theme \"${plugin#*#}\"."
123                         # A theme was enabled: So assume that this is a theme
124                         # and don't enable it as plugin.
125                         return 0
126                 fi
127                 echo "Trying to enable \"$1\" as plugin ..."
128         fi
129
130         for dname (
131                 "$plugin:A"
132                 "$AXZSH_PLUGIN_D/$plugin"
133                 "$ZSH_CUSTOM/$plugin"
134                 "$AXZSH/custom_plugins/$plugin"
135                 "$AXZSH/repos/$plugin"
136                 "$AXZSH/plugins/$plugin"
137                 "$AXZSH/default_plugins/$plugin"
138                 "$AXZSH/core/$plugin"
139         ); do
140                 [[ ! -d "$dname" ]] && continue
141                 mkdir -p "$dir"
142                 if ! (
143                         cd "$dir" || exit 9
144                         ln -s "$dname" "$PWD"
145                 ); then
146                         ax_error "Failed to create link!"
147                         return 1
148                 fi
149                 ax_msg 0 "Plugin \"$plugin\" enabled."
150                 return 0
151         done
152
153         ax_error "Plugin \"$1\" not found!"
154         return 1
155 }
156
157 function DisablePlugin {
158         local plugin=$(NormalizedPluginName "$1")
159         local dir="$AXZSH/active_plugins"
160         local r=-1
161
162         # Active theme?
163         if [[ $(readlink "$AXZSH/active_theme") = "$AXZSH/repos/$plugin/"* ]]; then
164                 rm "$AXZSH/active_theme"; r=$?
165         fi
166
167         # Active plugin?
168         if [[ -h "$dir/$plugin" ]]; then
169                 rm "$dir/$plugin"; r=$?
170         fi
171
172         if [[ $r -eq -1 ]]; then
173                 ax_msg 1 "Plugin \"$1\" not active, nothing to do?"
174                 r=1
175         fi
176
177         if [[ "$plugin" = *"#"* ]]; then
178                 # Name matches a cloned repository, try to clean up!
179                 echo "Cleaning up cloned repository ..."
180                 rm -fr "$AXZSH/repos/$plugin"
181         fi
182
183         return $r
184 }
185
186 function ListEnabledPlugins {
187         for plugin ($AXZSH/active_plugins/*(N)); do
188                 print ${plugin:t:s/#/\//}
189         done
190         return 0
191 }
192
193 function ResetPlugins {
194         local dir="$AXZSH/active_plugins"
195         local r1=0, r2=0
196
197         if [[ -e "$dir" ]]; then
198                 ax_msg - "Removing all symbolic links in $dir ..."
199                 find "$dir" -type l -print -delete; r1=$?
200         fi
201
202         ax_msg - "Removing all external repositories in \"$AXZSH/repos\" ..."
203         rm -fr "$AXZSH/repos"; r2=$?
204
205         [[ $r1 == 0 && $r2 == 0 ]] && return 0 || return 1
206 }
207
208 function EnableDefaultPlugins {
209         local dir="$AXZSH/active_plugins"
210
211         ax_msg - "Activating (linking) default plugins ..."
212         mkdir -p "$dir"
213         (
214                 cd "$dir" || exit 9
215                 ln -sfv "$AXZSH/default_plugins/"* "$PWD"
216         )
217         return $?
218 }
219
220 function SetTheme {
221         local link_name="$AXZSH/active_theme"
222
223         if [ "$1" = "-" ]; then
224                 rm -f "$link_name" || return 1
225                 ax_msg 0 "Theme settings have been reset."
226                 return 0
227         fi
228
229         if [ -r "$1" ]; then
230                 theme="$1"
231         elif [ -r "$AXZSH/custom_themes/$1.axzshtheme" ]; then
232                 theme="$AXZSH/custom_themes/$1.axzshtheme"
233         elif [ -r "$AXZSH/themes/$1.axzshtheme" ]; then
234                 theme="$AXZSH/themes/$1.axzshtheme"
235         else
236                 # Look for theme inside of installed plugins:
237                 for dname (
238                         "$AXZSH/custom_themes"
239                         "$AXZSH/custom_plugins/"*(N)
240                         "$AXZSH/repos/"*(N)
241                 ); do
242                         if [[ -r "$dname/$1.axzshtheme" ]]; then
243                                 theme="$dname/$1.axzshtheme"
244                                 break
245                         elif [[ -r "$dname/$1.zsh-theme" ]]; then
246                                 theme="$dname/$1.zsh-theme"
247                                 break
248                         fi
249                 done
250                 if [[ -z "$theme" ]]; then
251                         ax_error "Theme \"$1\" not found!"
252                         return 1
253                 fi
254         fi
255         ln -fsv "$theme" "$link_name" || return 1
256         return $?
257 }
258
259 function UpgradeAXZSH {
260         if [[ $+commands[git] -eq 0 ]]; then
261                 ax_error "The git(1) command is not available!"
262                 return 1
263         fi
264         if [[ ! -d "$AXZSH/.git" ]]; then
265                 ax_error "AX-ZSH seems not to be installed using Git. Can't upgrade!"
266                 return 1
267         fi
268
269         ax_msg - "Upgrading AX-ZSH in \"$AXZSH\" using git(1) ..."
270         ( cd "$AXZSH" && git pull --ff-only )
271 }
272
273 function UpgradeForeignPlugins {
274         if [[ $+commands[git] -eq 0 ]]; then
275                 ax_error "The git(1) command is not available!"
276                 return 1
277         fi
278
279         for dir ($AXZSH/repos/*(N)); do
280                 name=${dir:t:s/#/\//}
281                 if [ -d "$dir/.git" ]; then
282                         ax_msg - "Upgrading \"$name\" [git] ..."
283                         (
284                                 cd "$dir"
285                                 git pull --ff-only || ax_error "Pull failed!"
286                         )
287                 else
288                         ax_error "Unknown repository type!"
289                 fi
290         done
291 }
292
293 function CheckPlugins {
294         missing_plugins=()
295         invalid_plugins=()
296
297         ax_msg - "Checking plugins ..."
298         for dir ($AXZSH/plugins/*(N)); do
299                 plugin=${dir:t}
300
301                 # Test if plugin is already enabled
302                 [[ -e "$AXZSH/active_plugins/$plugin" ]] \
303                         && enabled=" (enabled)" \
304                         || unset enabled
305
306                 # Test plugin ...
307                 printf " - \"%s\"%s ... " "$plugin" "$enabled"
308                 new_plugin=""
309                 for script ($AXZSH/plugins/$plugin/$plugin.{zshrc,zprofile}); do
310                         [[ -r "$script" ]] || continue
311                         AXZSH_PLUGIN_CHECK=1 zsh -i -c "source $script"; r=$?
312                         if [[ $r -eq 0 ]]; then
313                                 new_plugin=$plugin
314                                 break
315                         fi
316                 done
317                 if [[ -n "$new_plugin" ]]; then
318                         detected_plugins+=($new_plugin)
319                         [[ -n "$enabled" ]] || missing_plugins+=($new_plugin)
320                         ax_msg 0 "OK."
321                 elif [[ $r -eq 91 ]]; then
322                         ax_msg 1 "ignored."
323                 elif [[ $r -eq 92 ]]; then
324                         ax_msg 1 "optional."
325                 else
326                         [[ -n "$enabled" ]] && invalid_plugins+=($plugin)
327                         ax_msg 2 "failed."
328                 fi
329         done
330         echo
331
332         result=0
333         if [[ -n "$missing_plugins" ]]; then
334                 ax_msg 1 "Run the following command to enable all missing plugins:"
335                 echo "$AXZSH/bin/axzshctl enable-plugin" $missing_plugins
336                 echo
337                 result=1
338         else
339                 ax_msg 0 "All detected plugins are already enabled."
340         fi
341
342         if [[ -n "$invalid_plugins" ]]; then
343                 ax_msg 1 "Run the following command to disable all failed plugins:"
344                 echo "$AXZSH/bin/axzshctl disable-plugin" $invalid_plugins
345                 result=1
346         else
347                 ax_msg 0 "No failed plugins are enabled."
348         fi
349
350         echo
351         return $result
352 }
353
354 NAME="$0:t"
355
356 [[ $# -gt 0 ]] || Usage
357
358 if [[ -z "$AXZSH" || ! -r "$AXZSH/ax.zsh" ]]; then
359         [[ -r "$HOME/.axzsh/ax.zsh" ]] && AXZSH="$HOME/.axzsh"
360         if [[ ! -r "$AXZSH/ax.zsh" ]]; then
361                 ax_error "Oops, \"AXZSH\" is not set or invalid and can't be autodetected!"
362                 exit 3
363         fi
364 fi
365
366 cmd="$1"
367 shift
368
369 case "$cmd" in
370         "enable")
371                 [[ $# -eq 0 ]] || Usage
372                 EnableAXZSH
373                 ;;
374         "disable")
375                 [[ $# -eq 0 ]] || Usage
376                 DisableAXZSH
377                 ;;
378         "enable-plugin")
379                 [[ $# -gt 0 ]] || Usage
380                 for plugin in "$@"; do
381                         EnablePlugin "$plugin"
382                 done
383                 UpdatePluginCache
384                 ;;
385         "disable-plugin")
386                 [[ $# -gt 0 ]] || Usage
387                 for plugin in "$@"; do
388                         DisablePlugin "$plugin"
389                 done
390                 UpdatePluginCache
391                 ;;
392         "list-enabled")
393                 [[ $# -eq 0 ]] || Usage
394                 ListEnabledPlugins
395                 ;;
396         "reset-plugins")
397                 [[ $# -eq 0 ]] || Usage
398                 ResetPlugins
399                 EnableDefaultPlugins
400                 UpdatePluginCache
401                 ;;
402         "enable-default-plugins")
403                 [[ $# -eq 0 ]] || Usage
404                 EnableDefaultPlugins && UpdatePluginCache
405                 ;;
406         "check-plugins")
407                 [[ $# -eq 0 ]] || Usage
408                 CheckPlugins
409                 ;;
410         "set-theme")
411                 [[ $# -eq 1 ]] || Usage
412                 SetTheme "$1"
413                 ;;
414         "upgrade")
415                 [[ $# -eq 0 ]] || Usage
416                 UpgradeAXZSH
417                 UpgradeForeignPlugins
418                 UpdatePluginCache
419                 ;;
420         "update-caches")
421                 [[ $# -eq 0 ]] || Usage
422                 UpdatePluginCache
423                 ;;
424         "--help")
425                 Usage
426                 ;;
427         *)
428                 ax_error "Invalid command \"$cmd\"!"
429                 ax_error "Try \"$0 --help\" for more information."
430                 exit 2
431 esac