Skip to content

Commit f8a8970

Browse files
authored
Merge pull request #152 from mbland/modules-1-of-2
Refactor `./go modules`, list project modules before plugins
2 parents 6fd0eab + c8f6a4c commit f8a8970

File tree

6 files changed

+206
-168
lines changed

6 files changed

+206
-168
lines changed

lib/strings

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@ export __GO_STRINGS_VALIDATION_SKIP_CALLERS=2
2727
# Splits fields from a delimited string into an array defined by the caller
2828
#
2929
# While `IFS= read -ra array_name <<<"$value"` is idiomatic, this function
30-
# guards against a bug in Bash 4.2.25 (the version in the Ubuntu Precise image
31-
# used by Travis CI) and not fixed until 4.2.41 whereby the temporary
32-
# environment of `IFS= read` isn't honored when running in a process
30+
# handles two situations. First, it handles splitting strings whose items
31+
# themselves contain newline characters.
32+
#
33+
# Second, it guards against a bug in Bash 4.2.25 (the version in the Ubuntu
34+
# Precise image used by Travis CI) and not fixed until 4.2.41 whereby the
35+
# temporary environment of `IFS= read` isn't honored when running in a process
3336
# substitution. For details, see the message for commit
3437
# 99ab7805e6ef0a14568d8a100eec03bb2cb03631.
3538
#
@@ -46,7 +49,22 @@ export __GO_STRINGS_VALIDATION_SKIP_CALLERS=2
4649
@go.split() {
4750
@go.validate_identifier_or_die 'Result array name' "$3"
4851
local IFS="$1"
49-
read -ra "$3" <<<"$2"
52+
53+
# There are a few subtle interactions handled here.
54+
#
55+
# To handle items containing newlines, we set `-d ''` so that `read` will
56+
# read the entire `value` string (i.e. `$2`). To avoid an EOF error on what
57+
# should be a successful read with `-d ''`, we set `-n "${#2}"` to read
58+
# exactly the number of characters in `value`.
59+
#
60+
# When `value` is the empty string, `<<<` will add a newline which will be
61+
# read even with `read -n 0` being in effect. Consequently, we `unset` the
62+
# result array explicitly (something normally handled by `read -a`) and only
63+
# call `read` when the string is not empty.
64+
unset "$3"
65+
if [[ "${#2}" -ne '0' ]]; then
66+
read -ra "$3" -n "${#2}" -d '' <<<"$2"
67+
fi
5068
}
5169

5270
# Joins multiple items into a string variable defined by the caller

libexec/modules

Lines changed: 106 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,25 @@
4646
# For detailed information about the module system, run `{{go}} {{cmd}} --help`
4747
# without a `<module-name>` argument.
4848

49+
# Imports the functions from `lib/internal/use`. Since the last line invokes
50+
# `_@go.use_modules`, this wrapper ensures the argument list is empty to avoid
51+
# having it attempt to process this command script's argument array.
52+
_@go.modules_import_use_modules_functions() {
53+
. "$_GO_USE_MODULES"
54+
}
55+
_@go.modules_import_use_modules_functions
56+
4957
_@go.modules_help() {
50-
local module_name="$1"
51-
local __go_module_path
58+
local __go_module_name="$1"
59+
local __go_module_file
5260

5361
if [[ "$#" -eq '0' ]]; then
54-
module_name='$_GO_USE_MODULES'
55-
__go_module_path="$_GO_USE_MODULES"
62+
__go_module_name='$_GO_USE_MODULES'
63+
__go_module_file="$_GO_USE_MODULES"
5664
elif [[ "$#" -ne '1' ]]; then
5765
@go.printf "Please specify only one module name.\n" >&2
5866
return 1
59-
elif ! _@go.modules_path "$module_name"; then
67+
elif ! _@go.find_module; then
6068
@go.printf "Unknown module: $1\n" >&2
6169
return 1
6270
fi
@@ -65,33 +73,12 @@ _@go.modules_help() {
6573

6674
. "$_GO_CORE_DIR/lib/internal/command_descriptions"
6775

68-
if ! _@go.command_description "$__go_module_path"; then
76+
if ! _@go.command_description "$__go_module_file"; then
6977
@go.printf "ERROR: failed to parse description from %s\n" \
70-
"$__go_module_path" >&2
78+
"$__go_module_file" >&2
7179
return 1
7280
fi
73-
@go.printf "$module_name - $__go_cmd_desc\n"
74-
}
75-
76-
_@go.modules_path() {
77-
local module_name="$1"
78-
79-
__go_module_path="$_GO_CORE_DIR/lib/$module_name"
80-
if [[ -f "$__go_module_path" ]]; then
81-
return
82-
fi
83-
84-
# Convert <plugin>/<module> to _GO_PLUGINS_DIR/<plugin>/lib/<module>
85-
__go_module_path="$_GO_PLUGINS_DIR/${module_name/\///lib/}"
86-
if [[ -n "$_GO_PLUGINS_DIR" && -f "$__go_module_path" ]]; then
87-
return
88-
fi
89-
90-
__go_module_path="$_GO_SCRIPTS_DIR/lib/$module_name"
91-
if [[ -f "$__go_module_path" ]]; then
92-
return
93-
fi
94-
return 1
81+
@go.printf "$__go_module_name - $__go_cmd_desc\n"
9582
}
9683

9784
_@go.modules_find_all_in_dir() {
@@ -149,33 +136,34 @@ _@go.modules_produce_listing() {
149136
return
150137
fi
151138

152-
. "$_GO_CORE_DIR/lib/format"
153-
154-
local padded_modules=()
155-
local zipped_modules=()
156-
157-
@go.pad_items padded_modules "${modules[@]}"
158-
modules=("${padded_modules[@]}")
139+
. "$_GO_USE_MODULES" 'format'
140+
@go.pad_items modules "${modules[@]}"
159141

160142
case "$action" in
161143
paths)
162144
local relative_paths=("${__go_modules[@]#$_GO_ROOTDIR/}")
163-
@go.zip_items modules relative_paths ' ' zipped_modules
145+
@go.zip_items modules relative_paths ' ' modules
164146
;;
165147
summaries)
166148
local __go_modules_summaries=()
167149
if ! _@go.modules_summaries; then
168150
return 1
169151
fi
170-
@go.zip_items modules __go_modules_summaries ' ' zipped_modules
152+
@go.zip_items modules __go_modules_summaries ' ' modules
171153
;;
172154
*)
173155
# Should only happen if _@go.modules is updated and this case statement
174156
# isn't.
175157
@go.printf 'ERROR: Unknown action: %s\n' "$action" >&2
176158
return 1
177159
esac
178-
__go_modules_listing=("${zipped_modules[@]}")
160+
__go_modules_listing=("${modules[@]}")
161+
}
162+
163+
_@go.modules_search_plugins() {
164+
for plugin in "$1/"${plugin_glob[0]:-*}; do
165+
_@go.modules_find_all_in_dir "$plugin" "${plugin_glob[1]:-*}"
166+
done
179167
}
180168

181169
_@go.modules_search() {
@@ -194,15 +182,11 @@ _@go.modules_search() {
194182
_@go.modules_find_all_in_dir "$_GO_CORE_DIR" "$glob"
195183
__go_core_modules_end="${#__go_modules[@]}"
196184

197-
if [[ -n "$_GO_PLUGINS_DIR" ]]; then
198-
for plugin in "$_GO_PLUGINS_DIR/"${plugin_glob[0]:-*}; do
199-
_@go.modules_find_all_in_dir "$plugin" "${plugin_glob[1]:-*}"
200-
done
201-
fi
202-
__go_plugin_modules_end="${#__go_modules[@]}"
203-
204185
_@go.modules_find_all_in_dir "$_GO_SCRIPTS_DIR" "$glob"
205186
__go_project_modules_end="${#__go_modules[@]}"
187+
188+
@go.search_plugins '_@go.modules_search_plugins'
189+
__go_plugin_modules_end="${#__go_modules[@]}"
206190
}
207191

208192
_@go.modules_emit_class() {
@@ -215,61 +199,90 @@ _@go.modules_emit_class() {
215199

216200
if [[ "${#__go_modules[@]}" -ne '0' ]] &&
217201
_@go.modules_produce_listing "$action"; then
218-
local IFS=$'\n'
219-
printf "From the %s:\n%s\n\n" "$class" "${__go_modules_listing[*]/#/ }"
202+
printf 'From the %s:\n' "$class"
203+
printf '%s\n' "${__go_modules_listing[@]/#/ }" ''
220204
fi
221205
}
222206

223207
_@go.modules_list_by_class() {
224208
local action="$1"
225209
local __go_modules=()
226210
local __go_core_modules_end=0
227-
local __go_plugin_modules_end=0
228211
local __go_project_modules_end=0
212+
local __go_plugin_modules_end=0
229213
local __go_all_modules
230214

231215
_@go.modules_search
232216
__go_all_modules=("${__go_modules[@]}")
233217
_@go.modules_emit_class 'core framework library' "$action" \
234218
0 "$__go_core_modules_end"
235-
_@go.modules_emit_class 'installed plugin libraries' "$action" \
236-
"$__go_core_modules_end" "$__go_plugin_modules_end"
237219
_@go.modules_emit_class 'project library' "$action" \
238-
"$__go_plugin_modules_end" "$__go_project_modules_end"
220+
"$__go_core_modules_end" "$__go_project_modules_end"
221+
_@go.modules_emit_class 'installed plugin libraries' "$action" \
222+
"$__go_project_modules_end" "$__go_plugin_modules_end"
239223
}
240224

241225
_@go.modules_list() {
242226
local action="$1"
243227
shift
244228
local module_specs=("$@")
245229
local __go_modules=()
246-
local __go_module_path
247-
local module_spec
230+
local __go_module_name
231+
local __go_module_file
248232

249-
for module_spec in "${module_specs[@]}"; do
250-
if [[ "$module_spec" == '*' ]]; then
233+
for __go_module_name in "${module_specs[@]}"; do
234+
if [[ "$__go_module_name" == '*' ]]; then
251235
if [[ "${#module_specs[@]}" -ne 1 ]]; then
252236
@go.printf "Do not specify other patterns when '*' is present.\n" >&2
253237
return 1
254238
fi
255239
_@go.modules_search
256-
elif [[ "$module_spec" =~ \*|/$ ]]; then
257-
_@go.modules_search "$module_spec"
258-
elif ! _@go.modules_path "$module_spec"; then
259-
@go.printf "Unknown module: $module_spec\n" >&2
240+
elif [[ "$__go_module_name" =~ \*|/$ ]]; then
241+
_@go.modules_search "$__go_module_name"
242+
elif ! _@go.find_module; then
243+
@go.printf "Unknown module: $__go_module_name\n" >&2
260244
return 1
261245
else
262-
__go_modules+=("$__go_module_path")
246+
__go_modules+=("$__go_module_file")
263247
fi
264248
done
265249

266250
local __go_modules_listing=()
267-
if _@go.modules_produce_listing "$action"; then
268-
local IFS=$'\n'
269-
echo "${__go_modules_listing[*]}"
270-
else
251+
if ! _@go.modules_produce_listing "$action"; then
271252
return 1
272253
fi
254+
printf '%s\n' "${__go_modules_listing[@]}"
255+
}
256+
257+
_@go.modules_tab_completion_plugins() {
258+
local plugin
259+
260+
# `glob` indicates specific plugin module.
261+
if [[ "$glob" =~ / ]]; then
262+
plugin="${glob%/*}"
263+
_@go.modules_find_all_in_dir "$1/$plugin" "${glob#*/}*"
264+
return
265+
fi
266+
267+
# Otherwise collect all plugin modules and the names of the plugins to
268+
# which they belong. Which set gets returned will be decided at the end.
269+
for plugin in "$1"/$glob; do
270+
_@go.modules_find_all_in_dir "$plugin"
271+
if [[ "${#__go_modules[@]}" -eq '0' ]]; then
272+
continue
273+
fi
274+
275+
__go_modules=("${__go_modules[@]#$1/}")
276+
__go_modules=("${__go_modules[@]/\/lib\///}")
277+
@go.complete_remove_completions_already_present 'args' '__go_modules' \
278+
"${#__go_modules[@]}"
279+
280+
if [[ "${#__go_modules[@]}" -ne '0' ]]; then
281+
plugins+=("$plugin/")
282+
plugin_modules+=("${__go_modules[@]}")
283+
__go_modules=()
284+
fi
285+
done
273286
}
274287

275288
_@go.modules_tab_completion() {
@@ -304,37 +317,7 @@ _@go.modules_tab_completion() {
304317
local plugin_modules=()
305318

306319
. "$_GO_USE_MODULES" 'complete'
307-
308-
if [[ -n "$_GO_PLUGINS_DIR" ]]; then
309-
local plugin
310-
311-
# Complete a specific plugin module (word contains a '/').
312-
if [[ "$glob" =~ / ]]; then
313-
plugin="${glob%/*}"
314-
_@go.modules_find_all_in_dir "$_GO_PLUGINS_DIR/$plugin" "${glob#*/}*"
315-
316-
else
317-
# Otherwise collect all plugin modules and the names of the plugins to
318-
# which they belong. Which set gets returned will be decided at the end.
319-
for plugin in "$_GO_PLUGINS_DIR"/$glob; do
320-
_@go.modules_find_all_in_dir "$plugin"
321-
if [[ "${#__go_modules[@]}" -eq '0' ]]; then
322-
continue
323-
fi
324-
325-
__go_modules=("${__go_modules[@]#$_GO_PLUGINS_DIR/}")
326-
__go_modules=("${__go_modules[@]/\/lib\///}")
327-
@go.complete_remove_completions_already_present \
328-
'args' '__go_modules' "${#__go_modules[@]}"
329-
330-
if [[ "${#__go_modules[@]}" -ne '0' ]]; then
331-
plugins+=("$plugin/")
332-
plugin_modules+=("${__go_modules[@]}")
333-
__go_modules=()
334-
fi
335-
done
336-
fi
337-
fi
320+
@go.search_plugins '_@go.modules_tab_completion_plugins'
338321

339322
# Don't search other dirs if completing a plugin (word contains a '/').
340323
if [[ ! "$glob" =~ / ]]; then
@@ -352,15 +335,35 @@ _@go.modules_tab_completion() {
352335
fi
353336

354337
local __go_modules_listing=()
355-
if _@go.modules_produce_listing; then
356-
completions+=("${__go_modules_listing[@]}")
357-
@go.complete_remove_completions_already_present \
358-
'args' 'completions' "${#completions[@]}"
359-
echo "${completions[@]}"
360-
else
338+
if ! _@go.modules_produce_listing; then
361339
# Shouldn't happen, since modules_produce_listing isn't parsing summaries.
362340
return 1
363341
fi
342+
completions+=("${__go_modules_listing[@]}")
343+
@go.complete_remove_completions_already_present \
344+
'args' 'completions' "${#completions[@]}"
345+
echo "${completions[@]}"
346+
}
347+
348+
_@go.modules_list_imported() {
349+
if [[ "$#" -ne 0 ]]; then
350+
@go.printf 'The --imported option takes no other arguments.\n' >&2
351+
return 1
352+
fi
353+
354+
# Collect imported module info before _GO_USE_MODULES imports possibly more.
355+
local imported_modules=("${_GO_IMPORTED_MODULES[@]}")
356+
local imported_files=("${_GO_IMPORTED_MODULE_FILES[@]#$_GO_ROOTDIR/}")
357+
local imported_callers=("${_GO_IMPORTED_MODULE_CALLERS[@]#$_GO_ROOTDIR/}")
358+
local caller_pad
359+
local imported_listing=()
360+
361+
. "$_GO_USE_MODULES" 'format'
362+
@go.pad_items imported_modules "${imported_modules[@]}"
363+
@go.zip_items imported_modules imported_files ' ' imported_listing
364+
printf -v 'caller_pad' -- "\n%$((${#imported_modules[0]} + 4))s" ''
365+
@go.zip_items imported_listing imported_callers "$caller_pad" imported_listing
366+
printf '%s\n' "${imported_listing[@]}"
364367
}
365368

366369
_@go.modules() {
@@ -384,12 +387,7 @@ _@go.modules() {
384387
fi
385388
;;
386389
--imported)
387-
if [[ "$#" -ne 0 ]]; then
388-
@go.printf 'The --imported option takes no other arguments.\n' >&2
389-
return 1
390-
elif [[ "${#_GO_IMPORTED_MODULES[@]}" -ne '0' ]]; then
391-
_@go.modules_list 'paths' "${_GO_IMPORTED_MODULES[@]}"
392-
fi
390+
_@go.modules_list_imported "$@"
393391
;;
394392
-*)
395393
@go.printf "Unknown option: $action\n" >&2

tests/modules/help.bats

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ load ../environment
44
load helpers
55

66
setup() {
7+
test_filter
78
@go.create_test_go_script '@go "$@"'
89
setup_test_modules
910
}

0 commit comments

Comments
 (0)