Skip to content

Commit

Permalink
WIP: Add modules builtin command
Browse files Browse the repository at this point in the history
  • Loading branch information
mbland committed Oct 27, 2016
1 parent a5973af commit 30dd4f8
Show file tree
Hide file tree
Showing 3 changed files with 330 additions and 2 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,7 @@ plugins, or your own project's scripts directory by sourcing the
. $_GO_USE_MODULES 'log'
```

See the header comment in [lib/internal/use](lib/internal/use) for more
information.
Run `./go help modules` and `./go modules help` for more information.

### Feedback and contributions

Expand Down
8 changes: 8 additions & 0 deletions lib/internal/use
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
# Module loading is idempotent, as the names of imported modules are added to
# the `_GO_IMPORTED_MODULES` array and are not sourced again if their names are
# already in the array.
#
# To see what modules are currently imported and their corresponding files, use:
#
# for modules imported by the top-level `./go` script:
# {{go}} modules --imported
#
# for modules imported within a Bash command script or function:
# @go modules --imported

declare __go_module_name
declare __go_loaded_module
Expand Down
321 changes: 321 additions & 0 deletions libexec/modules
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
#! /bin/bash
#
# List optional Bash modules available for import via `. $_GO_USE_MODULES`
#
# Usage:
# A list of all available plugins by class (core, plugin, project):
# {{go}} {{cmd}} [--paths|--summaries]
#
# The paths or one-line summaries for all or individual modules:
# {{go}} {{cmd}} [--paths|--summaries] <module-glob...>
#
# The modules currently imported by the script and their source paths:
# {{go}} {{cmd}} --imported
#
# Detailed help for an individual module:
# {{go}} {{cmd}} [-h|-help|--help|help] <module-name>
#
# Detailed help for the module system itself:
# {{go}} {{cmd}} [-h|-help|--help|help]
#
# Options:
# -h,--help Show the help message for a specific module
# --paths List the path of each module
# --summaries List the summary of each module
#
# Where:
# <module-name> Name of one of the installed modules
# <module-glob> Module name, plugin package (trailing /), or glob pattern
#
# Modules are reusable libraries of Bash code that may be sourced by the
# top-level `./go` script, by individual Bash command scripts, and individual
# Bash functions by executing `. $_GO_USE_MODULES` followed by one or more
# module names.
#
# For detailed information about the module system, run `{{go}} {{cmd}} help`
# without a `<module-name>` argument.

. "$_GO_CORE_DIR/lib/internal/command_descriptions"

_@go.modules_help() {
local module_name="$1"
local __go_module_path

if [[ "$#" -eq '0' ]]; then
module_name='$_GO_USE_MODULES'
__go_module_path="$_GO_USE_MODULES"
elif [[ "$#" -ne '1' ]]; then
@go.printf "Please specify only one module name.\n" >&2
return 1
elif ! _@go.modules_path "$module_name"; then
@go.printf "Unknown module: $1\n" >&2
return 1
fi

local __go_cmd_desc

if ! _@go.command_description "$__go_module_path"; then
@go.printf "ERROR: failed to parse description from %s\n" \
"$__go_cmd_path" >&2
return 1
fi
@go.printf "$module_name - $__go_cmd_desc\n"
}

_@go.modules_path() {
local module_name="$1"

__go_module_path="$_GO_CORE_DIR/lib/$module_name"
if [[ -f "$__go_module_path" ]]; then
return
fi

# Convert <plugin>/<module> to _GO_SCRIPTS_DIR/plugins/<plugin>/lib/<module>
__go_module_path="$_GO_SCRIPTS_DIR/plugins/${module_name/\///lib/}"
if [[ -f "$__go_module_path" ]]; then
return
fi

__go_module_path="$_GO_SCRIPTS_DIR/lib/$module_name"
if [[ -f "$__go_module_path" ]]; then
return
fi
return 1
}

_@go.modules_find_all_in_dir() {
local module_dir="$1"
local glob="${2:-*}"
local module_path
local __go_cmd_desc

for module_path in "$module_dir"/lib/$glob; do
if [[ -f "$module_path" ]]; then
__go_modules_paths+=("$module_path")
module="${module_path##*/lib/}"
__go_modules+=("$module")
fi
done
}

_@go.modules_add_info_to_module_names() {
local action="$1"
local padding=''
local padding_len=0
local module
local i

if [[ -z "$action" ]]; then
return
fi

for module in "${__go_modules[@]}"; do
while [[ "${#padding}" -lt "${#module}" ]]; do
padding+=' '
done
done

for ((i=0; i != "${#__go_modules[@]}"; ++i)); do
module="${__go_modules[$i]}"
padding_len="$((${#padding} - ${#module}))"
__go_modules["$i"]="${module}${padding:0:padding_len} "
done

case "$action" in
paths)
for ((i=0; i != "${#__go_modules[@]}"; ++i)); do
__go_modules["$i"]+="${__go_modules_paths[$i]#$_GO_ROOTDIR/}"
done
;;
summaries)
for ((i=0; i != "${#__go_modules[@]}"; ++i)); do
if ! _@go.command_summary "${__go_modules_paths[$i]}"; then
@go.printf "ERROR: failed to parse summary from %s\n" \
"$__go_cmd_path" >&2
return 1
fi
__go_modules["$i"]+="$__go_cmd_desc"
done
;;
*)
@go.printf "ERROR: Unknown action: $action\n" >&2
return 1
esac
}

_@go.modules_search() {
local action="$1"
local glob="${2:-*}"
local plugin
local plugin_glob=('*' "$glob")
local module_paths=()
local plugin_modules=()
local __go_cmd_desc
local i

if [[ "$glob" =~ / ]]; then
plugin_glob[0]="${glob%/*}"
plugin_glob[1]="${glob#*/}"
fi

__go_modules=()
__go_modules_paths=()
_@go.modules_find_all_in_dir "$_GO_CORE_DIR" "$glob"
_@go.modules_add_info_to_module_names "$action"
__go_core_modules=("${__go_modules[@]}")
modules_paths+=("${__go_modules_paths[@]}")
__go_modules_paths=()

for plugin in "$_GO_SCRIPTS_DIR"/plugins/${plugin_glob[0]:-*}; do
__go_modules=()
_@go.modules_find_all_in_dir "$plugin" "${plugin_glob[1]:-*}"
plugin="${plugin#$_GO_SCRIPTS_DIR/plugins/}"
plugin_modules+=("${__go_modules[@]/#/$plugin/}")
done

__go_modules=("${plugin_modules[@]}")
_@go.modules_add_info_to_module_names "$action"
__go_plugin_modules=("${__go_modules[@]}")
modules_paths+=("${__go_modules_paths[@]}")
__go_modules_paths=()

__go_modules=()
_@go.modules_find_all_in_dir "$_GO_SCRIPTS_DIR" "$glob"
_@go.modules_add_info_to_module_names "$action"
__go_project_modules=("${__go_modules[@]}")
__go_modules=("${__go_core_modules[@]}" "${__go_plugin_modules[@]}"
"${__go_project_modules[@]}")
__go_modules_paths=("${modules_paths[@]}")
}

_@go.modules_list_by_class() {
local action="$1"
local __go_core_modules
local __go_plugin_modules
local __go_project_modules
local delimiter=$'\n'' '

_@go.modules_search "$action"

if [[ "${#__go_core_modules}" -ne '0' ]]; then
printf "From the core framework library:%s\n\n" \
"${__go_core_modules[*]/#/$delimiter}"
fi

if [[ "${#__go_plugin_modules}" -ne '0' ]]; then
printf "From installed plugin libraries:%s\n\n" \
"${__go_plugin_modules[*]/#/$delimiter}"
fi

if [[ "${#__go_project_modules}" -ne '0' ]]; then
printf "From the project library:%s\n\n" \
"${__go_project_modules[*]/#/$delimiter}"
fi
}

_@go.modules_list() {
local action="$1"
shift
local __go_modules=("$@")
local __go_modules_paths=()
local __go_module_path
local module

for module in "${__go_modules[@]}"; do
if [[ "$module" == '*' ]]; then
if [[ "${#__go_modules[@]}" -ne 1 ]]; then
@go.printf "Do not specify other arguments when '*' is present.\n" >&2
return 1
fi
_@go.modules_search
elif [[ "$module" =~ \*|/ ]]; then
_@go.modules_search '' "$module"
elif ! _@go.modules_path "$module"; then
@go.printf "Unknown module: $module\n" >&2
return 1
else
__go_modules_paths+=("$__go_module_path")
fi
done

_@go.modules_add_info_to_module_names "$action"
@go.printf "%s\n" "${__go_modules[@]}"
}

_@go.modules_tab_completion() {
local word_index="$1"
shift
local args=("$@")
local first="${args[0]}"
local word=("${args[$word_index]}")
local flags=('-h' '-help' '--help' 'help' '--paths' '--summaries')
local origIFS="$IFS"
local plugins=''
local flags_pattern

if [[ "$#" -le '1' ]]; then
flags+=('--imported')
fi

local IFS='|'
flags_pattern="^(${flags[*]})$"
IFS="$origIFS"

if [[ "$#" -eq '0' || "$word_index" -eq '0' ]]; then
echo "${flags[*]}"
return
elif [[ ! "$first" =~ $flags_pattern || "$first" == '--imported' ]]; then
return
fi

local __go_core_modules
local __go_plugin_modules
local __go_project_modules
_@go.modules_search "$action"

if [[ "$word" =~ / ]]; then
plugins="${__go_plugin_modules[*]}"
else
plugins="${__go_plugin_modules[*]/\/*//}"
fi
echo "${__go_core_modules[*]} ${plugins[*]} ${__go_project_modules[*]}"
}

_@go.modules() {
local action="$1"
shift

case "$action" in
--complete)
# Tab completions
_@go.modules_tab_completion "$@"
;;
-h|-help|--help|help)
_@go.modules_help "$@"
;;
''|--paths|--summaries)
action="${action#--}"
if [[ "$#" -eq '0' ]]; then
_@go.modules_list_by_class "$action"
else
_@go.modules_list "$action" "$@"
fi
;;
--imported)
if [[ "$#" -ne 0 ]]; then
@go.printf 'The --imported option takes no other arguments.\n' >&2
return 1
elif [[ "${#_GO_IMPORTED_MODULES[@]}" -ne '0' ]]; then
_@go.modules_list 'paths' "${_GO_IMPORTED_MODULES[@]}"
fi
;;
-*)
@go.printf "Unknown option: $action\n" >&2
return 1
;;
*)
_@go.modules_list '' "$action" "$@"
esac
}

_@go.modules "$@"

0 comments on commit 30dd4f8

Please sign in to comment.