···122122123123#### Create a tap repo
124124125125-Make a repo: `github.com/stormlightlabs/homebrew-tools`.
125125+Make a repo: `github.com/stormlightlabs/homebrew-tap`.
126126127127#### Formula template (`storm.rb`)
128128···153153154154```yaml
155155brews:
156156- - tap: stormlightlabs/homebrew-tools
156156+ - tap: stormlightlabs/homebrew-tap
157157 name: storm
158158 folder: Formula
159159 commit_author:
+11-2
README.md
···11111212## Install
13131414+### Homebrew (macOS / Linux)
1515+1616+```sh
1717+brew install stormlightlabs/tap/storm
1818+```
1919+2020+The goreleaser workflow keeps the [`stormlightlabs/homebrew-tap`](https://github.com/stormlightlabs/homebrew-tap)
2121+formula up to date.
2222+2323+### Go toolchain
2424+1425```sh
1526go install github.com/stormlightlabs/git-storm/cmd/storm@latest
1627```
1717-1818-(Need Homebrew? Use the `storm.rb` formula template in this repo to build a tap.)
19282029## Quick Start
2130
+426
completions/storm.bash
···11+# bash completion V2 for storm -*- shell-script -*-
22+33+__storm_debug()
44+{
55+ if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then
66+ echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
77+ fi
88+}
99+1010+# Macs have bash3 for which the bash-completion package doesn't include
1111+# _init_completion. This is a minimal version of that function.
1212+__storm_init_completion()
1313+{
1414+ COMPREPLY=()
1515+ _get_comp_words_by_ref "$@" cur prev words cword
1616+}
1717+1818+# This function calls the storm program to obtain the completion
1919+# results and the directive. It fills the 'out' and 'directive' vars.
2020+__storm_get_completion_results() {
2121+ local requestComp lastParam lastChar args
2222+2323+ # Prepare the command to request completions for the program.
2424+ # Calling ${words[0]} instead of directly storm allows handling aliases
2525+ args=("${words[@]:1}")
2626+ requestComp="${words[0]} __complete ${args[*]}"
2727+2828+ lastParam=${words[$((${#words[@]}-1))]}
2929+ lastChar=${lastParam:$((${#lastParam}-1)):1}
3030+ __storm_debug "lastParam ${lastParam}, lastChar ${lastChar}"
3131+3232+ if [[ -z ${cur} && ${lastChar} != = ]]; then
3333+ # If the last parameter is complete (there is a space following it)
3434+ # We add an extra empty parameter so we can indicate this to the go method.
3535+ __storm_debug "Adding extra empty parameter"
3636+ requestComp="${requestComp} ''"
3737+ fi
3838+3939+ # When completing a flag with an = (e.g., storm -n=<TAB>)
4040+ # bash focuses on the part after the =, so we need to remove
4141+ # the flag part from $cur
4242+ if [[ ${cur} == -*=* ]]; then
4343+ cur="${cur#*=}"
4444+ fi
4545+4646+ __storm_debug "Calling ${requestComp}"
4747+ # Use eval to handle any environment variables and such
4848+ out=$(eval "${requestComp}" 2>/dev/null)
4949+5050+ # Extract the directive integer at the very end of the output following a colon (:)
5151+ directive=${out##*:}
5252+ # Remove the directive
5353+ out=${out%:*}
5454+ if [[ ${directive} == "${out}" ]]; then
5555+ # There is not directive specified
5656+ directive=0
5757+ fi
5858+ __storm_debug "The completion directive is: ${directive}"
5959+ __storm_debug "The completions are: ${out}"
6060+}
6161+6262+__storm_process_completion_results() {
6363+ local shellCompDirectiveError=1
6464+ local shellCompDirectiveNoSpace=2
6565+ local shellCompDirectiveNoFileComp=4
6666+ local shellCompDirectiveFilterFileExt=8
6767+ local shellCompDirectiveFilterDirs=16
6868+ local shellCompDirectiveKeepOrder=32
6969+7070+ if (((directive & shellCompDirectiveError) != 0)); then
7171+ # Error code. No completion.
7272+ __storm_debug "Received error from custom completion go code"
7373+ return
7474+ else
7575+ if (((directive & shellCompDirectiveNoSpace) != 0)); then
7676+ if [[ $(type -t compopt) == builtin ]]; then
7777+ __storm_debug "Activating no space"
7878+ compopt -o nospace
7979+ else
8080+ __storm_debug "No space directive not supported in this version of bash"
8181+ fi
8282+ fi
8383+ if (((directive & shellCompDirectiveKeepOrder) != 0)); then
8484+ if [[ $(type -t compopt) == builtin ]]; then
8585+ # no sort isn't supported for bash less than < 4.4
8686+ if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
8787+ __storm_debug "No sort directive not supported in this version of bash"
8888+ else
8989+ __storm_debug "Activating keep order"
9090+ compopt -o nosort
9191+ fi
9292+ else
9393+ __storm_debug "No sort directive not supported in this version of bash"
9494+ fi
9595+ fi
9696+ if (((directive & shellCompDirectiveNoFileComp) != 0)); then
9797+ if [[ $(type -t compopt) == builtin ]]; then
9898+ __storm_debug "Activating no file completion"
9999+ compopt +o default
100100+ else
101101+ __storm_debug "No file completion directive not supported in this version of bash"
102102+ fi
103103+ fi
104104+ fi
105105+106106+ # Separate activeHelp from normal completions
107107+ local completions=()
108108+ local activeHelp=()
109109+ __storm_extract_activeHelp
110110+111111+ if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
112112+ # File extension filtering
113113+ local fullFilter="" filter filteringCmd
114114+115115+ # Do not use quotes around the $completions variable or else newline
116116+ # characters will be kept.
117117+ for filter in ${completions[*]}; do
118118+ fullFilter+="$filter|"
119119+ done
120120+121121+ filteringCmd="_filedir $fullFilter"
122122+ __storm_debug "File filtering command: $filteringCmd"
123123+ $filteringCmd
124124+ elif (((directive & shellCompDirectiveFilterDirs) != 0)); then
125125+ # File completion for directories only
126126+127127+ local subdir
128128+ subdir=${completions[0]}
129129+ if [[ -n $subdir ]]; then
130130+ __storm_debug "Listing directories in $subdir"
131131+ pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
132132+ else
133133+ __storm_debug "Listing directories in ."
134134+ _filedir -d
135135+ fi
136136+ else
137137+ __storm_handle_completion_types
138138+ fi
139139+140140+ __storm_handle_special_char "$cur" :
141141+ __storm_handle_special_char "$cur" =
142142+143143+ # Print the activeHelp statements before we finish
144144+ __storm_handle_activeHelp
145145+}
146146+147147+__storm_handle_activeHelp() {
148148+ # Print the activeHelp statements
149149+ if ((${#activeHelp[*]} != 0)); then
150150+ if [ -z $COMP_TYPE ]; then
151151+ # Bash v3 does not set the COMP_TYPE variable.
152152+ printf "\n";
153153+ printf "%s\n" "${activeHelp[@]}"
154154+ printf "\n"
155155+ __storm_reprint_commandLine
156156+ return
157157+ fi
158158+159159+ # Only print ActiveHelp on the second TAB press
160160+ if [ $COMP_TYPE -eq 63 ]; then
161161+ printf "\n"
162162+ printf "%s\n" "${activeHelp[@]}"
163163+164164+ if ((${#COMPREPLY[*]} == 0)); then
165165+ # When there are no completion choices from the program, file completion
166166+ # may kick in if the program has not disabled it; in such a case, we want
167167+ # to know if any files will match what the user typed, so that we know if
168168+ # there will be completions presented, so that we know how to handle ActiveHelp.
169169+ # To find out, we actually trigger the file completion ourselves;
170170+ # the call to _filedir will fill COMPREPLY if files match.
171171+ if (((directive & shellCompDirectiveNoFileComp) == 0)); then
172172+ __storm_debug "Listing files"
173173+ _filedir
174174+ fi
175175+ fi
176176+177177+ if ((${#COMPREPLY[*]} != 0)); then
178178+ # If there are completion choices to be shown, print a delimiter.
179179+ # Re-printing the command-line will automatically be done
180180+ # by the shell when it prints the completion choices.
181181+ printf -- "--"
182182+ else
183183+ # When there are no completion choices at all, we need
184184+ # to re-print the command-line since the shell will
185185+ # not be doing it itself.
186186+ __storm_reprint_commandLine
187187+ fi
188188+ elif [ $COMP_TYPE -eq 37 ] || [ $COMP_TYPE -eq 42 ]; then
189189+ # For completion type: menu-complete/menu-complete-backward and insert-completions
190190+ # the completions are immediately inserted into the command-line, so we first
191191+ # print the activeHelp message and reprint the command-line since the shell won't.
192192+ printf "\n"
193193+ printf "%s\n" "${activeHelp[@]}"
194194+195195+ __storm_reprint_commandLine
196196+ fi
197197+ fi
198198+}
199199+200200+__storm_reprint_commandLine() {
201201+ # The prompt format is only available from bash 4.4.
202202+ # We test if it is available before using it.
203203+ if (x=${PS1@P}) 2> /dev/null; then
204204+ printf "%s" "${PS1@P}${COMP_LINE[@]}"
205205+ else
206206+ # Can't print the prompt. Just print the
207207+ # text the user had typed, it is workable enough.
208208+ printf "%s" "${COMP_LINE[@]}"
209209+ fi
210210+}
211211+212212+# Separate activeHelp lines from real completions.
213213+# Fills the $activeHelp and $completions arrays.
214214+__storm_extract_activeHelp() {
215215+ local activeHelpMarker="_activeHelp_ "
216216+ local endIndex=${#activeHelpMarker}
217217+218218+ while IFS='' read -r comp; do
219219+ [[ -z $comp ]] && continue
220220+221221+ if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
222222+ comp=${comp:endIndex}
223223+ __storm_debug "ActiveHelp found: $comp"
224224+ if [[ -n $comp ]]; then
225225+ activeHelp+=("$comp")
226226+ fi
227227+ else
228228+ # Not an activeHelp line but a normal completion
229229+ completions+=("$comp")
230230+ fi
231231+ done <<<"${out}"
232232+}
233233+234234+__storm_handle_completion_types() {
235235+ __storm_debug "__storm_handle_completion_types: COMP_TYPE is $COMP_TYPE"
236236+237237+ case $COMP_TYPE in
238238+ 37|42)
239239+ # Type: menu-complete/menu-complete-backward and insert-completions
240240+ # If the user requested inserting one completion at a time, or all
241241+ # completions at once on the command-line we must remove the descriptions.
242242+ # https://github.com/spf13/cobra/issues/1508
243243+244244+ # If there are no completions, we don't need to do anything
245245+ (( ${#completions[@]} == 0 )) && return 0
246246+247247+ local tab=$'\t'
248248+249249+ # Strip any description and escape the completion to handled special characters
250250+ IFS=$'\n' read -ra completions -d '' < <(printf "%q\n" "${completions[@]%%$tab*}")
251251+252252+ # Only consider the completions that match
253253+ IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
254254+255255+ # compgen looses the escaping so we need to escape all completions again since they will
256256+ # all be inserted on the command-line.
257257+ IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%q\n" "${COMPREPLY[@]}")
258258+ ;;
259259+260260+ *)
261261+ # Type: complete (normal completion)
262262+ __storm_handle_standard_completion_case
263263+ ;;
264264+ esac
265265+}
266266+267267+__storm_handle_standard_completion_case() {
268268+ local tab=$'\t'
269269+270270+ # If there are no completions, we don't need to do anything
271271+ (( ${#completions[@]} == 0 )) && return 0
272272+273273+ # Short circuit to optimize if we don't have descriptions
274274+ if [[ "${completions[*]}" != *$tab* ]]; then
275275+ # First, escape the completions to handle special characters
276276+ IFS=$'\n' read -ra completions -d '' < <(printf "%q\n" "${completions[@]}")
277277+ # Only consider the completions that match what the user typed
278278+ IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
279279+280280+ # compgen looses the escaping so, if there is only a single completion, we need to
281281+ # escape it again because it will be inserted on the command-line. If there are multiple
282282+ # completions, we don't want to escape them because they will be printed in a list
283283+ # and we don't want to show escape characters in that list.
284284+ if (( ${#COMPREPLY[@]} == 1 )); then
285285+ COMPREPLY[0]=$(printf "%q" "${COMPREPLY[0]}")
286286+ fi
287287+ return 0
288288+ fi
289289+290290+ local longest=0
291291+ local compline
292292+ # Look for the longest completion so that we can format things nicely
293293+ while IFS='' read -r compline; do
294294+ [[ -z $compline ]] && continue
295295+296296+ # Before checking if the completion matches what the user typed,
297297+ # we need to strip any description and escape the completion to handle special
298298+ # characters because those escape characters are part of what the user typed.
299299+ # Don't call "printf" in a sub-shell because it will be much slower
300300+ # since we are in a loop.
301301+ printf -v comp "%q" "${compline%%$tab*}" &>/dev/null || comp=$(printf "%q" "${compline%%$tab*}")
302302+303303+ # Only consider the completions that match
304304+ [[ $comp == "$cur"* ]] || continue
305305+306306+ # The completions matches. Add it to the list of full completions including
307307+ # its description. We don't escape the completion because it may get printed
308308+ # in a list if there are more than one and we don't want show escape characters
309309+ # in that list.
310310+ COMPREPLY+=("$compline")
311311+312312+ # Strip any description before checking the length, and again, don't escape
313313+ # the completion because this length is only used when printing the completions
314314+ # in a list and we don't want show escape characters in that list.
315315+ comp=${compline%%$tab*}
316316+ if ((${#comp}>longest)); then
317317+ longest=${#comp}
318318+ fi
319319+ done < <(printf "%s\n" "${completions[@]}")
320320+321321+ # If there is a single completion left, remove the description text and escape any special characters
322322+ if ((${#COMPREPLY[*]} == 1)); then
323323+ __storm_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
324324+ COMPREPLY[0]=$(printf "%q" "${COMPREPLY[0]%%$tab*}")
325325+ __storm_debug "Removed description from single completion, which is now: ${COMPREPLY[0]}"
326326+ else
327327+ # Format the descriptions
328328+ __storm_format_comp_descriptions $longest
329329+ fi
330330+}
331331+332332+__storm_handle_special_char()
333333+{
334334+ local comp="$1"
335335+ local char=$2
336336+ if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
337337+ local word=${comp%"${comp##*${char}}"}
338338+ local idx=${#COMPREPLY[*]}
339339+ while ((--idx >= 0)); do
340340+ COMPREPLY[idx]=${COMPREPLY[idx]#"$word"}
341341+ done
342342+ fi
343343+}
344344+345345+__storm_format_comp_descriptions()
346346+{
347347+ local tab=$'\t'
348348+ local comp desc maxdesclength
349349+ local longest=$1
350350+351351+ local i ci
352352+ for ci in ${!COMPREPLY[*]}; do
353353+ comp=${COMPREPLY[ci]}
354354+ # Properly format the description string which follows a tab character if there is one
355355+ if [[ "$comp" == *$tab* ]]; then
356356+ __storm_debug "Original comp: $comp"
357357+ desc=${comp#*$tab}
358358+ comp=${comp%%$tab*}
359359+360360+ # $COLUMNS stores the current shell width.
361361+ # Remove an extra 4 because we add 2 spaces and 2 parentheses.
362362+ maxdesclength=$(( COLUMNS - longest - 4 ))
363363+364364+ # Make sure we can fit a description of at least 8 characters
365365+ # if we are to align the descriptions.
366366+ if ((maxdesclength > 8)); then
367367+ # Add the proper number of spaces to align the descriptions
368368+ for ((i = ${#comp} ; i < longest ; i++)); do
369369+ comp+=" "
370370+ done
371371+ else
372372+ # Don't pad the descriptions so we can fit more text after the completion
373373+ maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
374374+ fi
375375+376376+ # If there is enough space for any description text,
377377+ # truncate the descriptions that are too long for the shell width
378378+ if ((maxdesclength > 0)); then
379379+ if ((${#desc} > maxdesclength)); then
380380+ desc=${desc:0:$(( maxdesclength - 1 ))}
381381+ desc+="…"
382382+ fi
383383+ comp+=" ($desc)"
384384+ fi
385385+ COMPREPLY[ci]=$comp
386386+ __storm_debug "Final comp: $comp"
387387+ fi
388388+ done
389389+}
390390+391391+__start_storm()
392392+{
393393+ local cur prev words cword split
394394+395395+ COMPREPLY=()
396396+397397+ # Call _init_completion from the bash-completion package
398398+ # to prepare the arguments properly
399399+ if declare -F _init_completion >/dev/null 2>&1; then
400400+ _init_completion -n =: || return
401401+ else
402402+ __storm_init_completion -n =: || return
403403+ fi
404404+405405+ __storm_debug
406406+ __storm_debug "========= starting completion logic =========="
407407+ __storm_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword"
408408+409409+ # The user could have moved the cursor backwards on the command-line.
410410+ # We need to trigger completion from the $cword location, so we need
411411+ # to truncate the command-line ($words) up to the $cword location.
412412+ words=("${words[@]:0:$cword+1}")
413413+ __storm_debug "Truncated words[*]: ${words[*]},"
414414+415415+ local out directive
416416+ __storm_get_completion_results
417417+ __storm_process_completion_results
418418+}
419419+420420+if [[ $(type -t compopt) = "builtin" ]]; then
421421+ complete -o default -F __start_storm storm
422422+else
423423+ complete -o default -o nospace -F __start_storm storm
424424+fi
425425+426426+# ex: ts=4 sw=4 et filetype=sh
+235
completions/storm.fish
···11+# fish completion for storm -*- shell-script -*-
22+33+function __storm_debug
44+ set -l file "$BASH_COMP_DEBUG_FILE"
55+ if test -n "$file"
66+ echo "$argv" >> $file
77+ end
88+end
99+1010+function __storm_perform_completion
1111+ __storm_debug "Starting __storm_perform_completion"
1212+1313+ # Extract all args except the last one
1414+ set -l args (commandline -opc)
1515+ # Extract the last arg and escape it in case it is a space
1616+ set -l lastArg (string escape -- (commandline -ct))
1717+1818+ __storm_debug "args: $args"
1919+ __storm_debug "last arg: $lastArg"
2020+2121+ # Disable ActiveHelp which is not supported for fish shell
2222+ set -l requestComp "STORM_ACTIVE_HELP=0 $args[1] __complete $args[2..-1] $lastArg"
2323+2424+ __storm_debug "Calling $requestComp"
2525+ set -l results (eval $requestComp 2> /dev/null)
2626+2727+ # Some programs may output extra empty lines after the directive.
2828+ # Let's ignore them or else it will break completion.
2929+ # Ref: https://github.com/spf13/cobra/issues/1279
3030+ for line in $results[-1..1]
3131+ if test (string trim -- $line) = ""
3232+ # Found an empty line, remove it
3333+ set results $results[1..-2]
3434+ else
3535+ # Found non-empty line, we have our proper output
3636+ break
3737+ end
3838+ end
3939+4040+ set -l comps $results[1..-2]
4141+ set -l directiveLine $results[-1]
4242+4343+ # For Fish, when completing a flag with an = (e.g., <program> -n=<TAB>)
4444+ # completions must be prefixed with the flag
4545+ set -l flagPrefix (string match -r -- '-.*=' "$lastArg")
4646+4747+ __storm_debug "Comps: $comps"
4848+ __storm_debug "DirectiveLine: $directiveLine"
4949+ __storm_debug "flagPrefix: $flagPrefix"
5050+5151+ for comp in $comps
5252+ printf "%s%s\n" "$flagPrefix" "$comp"
5353+ end
5454+5555+ printf "%s\n" "$directiveLine"
5656+end
5757+5858+# this function limits calls to __storm_perform_completion, by caching the result behind $__storm_perform_completion_once_result
5959+function __storm_perform_completion_once
6060+ __storm_debug "Starting __storm_perform_completion_once"
6161+6262+ if test -n "$__storm_perform_completion_once_result"
6363+ __storm_debug "Seems like a valid result already exists, skipping __storm_perform_completion"
6464+ return 0
6565+ end
6666+6767+ set --global __storm_perform_completion_once_result (__storm_perform_completion)
6868+ if test -z "$__storm_perform_completion_once_result"
6969+ __storm_debug "No completions, probably due to a failure"
7070+ return 1
7171+ end
7272+7373+ __storm_debug "Performed completions and set __storm_perform_completion_once_result"
7474+ return 0
7575+end
7676+7777+# this function is used to clear the $__storm_perform_completion_once_result variable after completions are run
7878+function __storm_clear_perform_completion_once_result
7979+ __storm_debug ""
8080+ __storm_debug "========= clearing previously set __storm_perform_completion_once_result variable =========="
8181+ set --erase __storm_perform_completion_once_result
8282+ __storm_debug "Successfully erased the variable __storm_perform_completion_once_result"
8383+end
8484+8585+function __storm_requires_order_preservation
8686+ __storm_debug ""
8787+ __storm_debug "========= checking if order preservation is required =========="
8888+8989+ __storm_perform_completion_once
9090+ if test -z "$__storm_perform_completion_once_result"
9191+ __storm_debug "Error determining if order preservation is required"
9292+ return 1
9393+ end
9494+9595+ set -l directive (string sub --start 2 $__storm_perform_completion_once_result[-1])
9696+ __storm_debug "Directive is: $directive"
9797+9898+ set -l shellCompDirectiveKeepOrder 32
9999+ set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) % 2)
100100+ __storm_debug "Keeporder is: $keeporder"
101101+102102+ if test $keeporder -ne 0
103103+ __storm_debug "This does require order preservation"
104104+ return 0
105105+ end
106106+107107+ __storm_debug "This doesn't require order preservation"
108108+ return 1
109109+end
110110+111111+112112+# This function does two things:
113113+# - Obtain the completions and store them in the global __storm_comp_results
114114+# - Return false if file completion should be performed
115115+function __storm_prepare_completions
116116+ __storm_debug ""
117117+ __storm_debug "========= starting completion logic =========="
118118+119119+ # Start fresh
120120+ set --erase __storm_comp_results
121121+122122+ __storm_perform_completion_once
123123+ __storm_debug "Completion results: $__storm_perform_completion_once_result"
124124+125125+ if test -z "$__storm_perform_completion_once_result"
126126+ __storm_debug "No completion, probably due to a failure"
127127+ # Might as well do file completion, in case it helps
128128+ return 1
129129+ end
130130+131131+ set -l directive (string sub --start 2 $__storm_perform_completion_once_result[-1])
132132+ set --global __storm_comp_results $__storm_perform_completion_once_result[1..-2]
133133+134134+ __storm_debug "Completions are: $__storm_comp_results"
135135+ __storm_debug "Directive is: $directive"
136136+137137+ set -l shellCompDirectiveError 1
138138+ set -l shellCompDirectiveNoSpace 2
139139+ set -l shellCompDirectiveNoFileComp 4
140140+ set -l shellCompDirectiveFilterFileExt 8
141141+ set -l shellCompDirectiveFilterDirs 16
142142+143143+ if test -z "$directive"
144144+ set directive 0
145145+ end
146146+147147+ set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2)
148148+ if test $compErr -eq 1
149149+ __storm_debug "Received error directive: aborting."
150150+ # Might as well do file completion, in case it helps
151151+ return 1
152152+ end
153153+154154+ set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2)
155155+ set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2)
156156+ if test $filefilter -eq 1; or test $dirfilter -eq 1
157157+ __storm_debug "File extension filtering or directory filtering not supported"
158158+ # Do full file completion instead
159159+ return 1
160160+ end
161161+162162+ set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2)
163163+ set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2)
164164+165165+ __storm_debug "nospace: $nospace, nofiles: $nofiles"
166166+167167+ # If we want to prevent a space, or if file completion is NOT disabled,
168168+ # we need to count the number of valid completions.
169169+ # To do so, we will filter on prefix as the completions we have received
170170+ # may not already be filtered so as to allow fish to match on different
171171+ # criteria than the prefix.
172172+ if test $nospace -ne 0; or test $nofiles -eq 0
173173+ set -l prefix (commandline -t | string escape --style=regex)
174174+ __storm_debug "prefix: $prefix"
175175+176176+ set -l completions (string match -r -- "^$prefix.*" $__storm_comp_results)
177177+ set --global __storm_comp_results $completions
178178+ __storm_debug "Filtered completions are: $__storm_comp_results"
179179+180180+ # Important not to quote the variable for count to work
181181+ set -l numComps (count $__storm_comp_results)
182182+ __storm_debug "numComps: $numComps"
183183+184184+ if test $numComps -eq 1; and test $nospace -ne 0
185185+ # We must first split on \t to get rid of the descriptions to be
186186+ # able to check what the actual completion will be.
187187+ # We don't need descriptions anyway since there is only a single
188188+ # real completion which the shell will expand immediately.
189189+ set -l split (string split --max 1 \t $__storm_comp_results[1])
190190+191191+ # Fish won't add a space if the completion ends with any
192192+ # of the following characters: @=/:.,
193193+ set -l lastChar (string sub -s -1 -- $split)
194194+ if not string match -r -q "[@=/:.,]" -- "$lastChar"
195195+ # In other cases, to support the "nospace" directive we trick the shell
196196+ # by outputting an extra, longer completion.
197197+ __storm_debug "Adding second completion to perform nospace directive"
198198+ set --global __storm_comp_results $split[1] $split[1].
199199+ __storm_debug "Completions are now: $__storm_comp_results"
200200+ end
201201+ end
202202+203203+ if test $numComps -eq 0; and test $nofiles -eq 0
204204+ # To be consistent with bash and zsh, we only trigger file
205205+ # completion when there are no other completions
206206+ __storm_debug "Requesting file completion"
207207+ return 1
208208+ end
209209+ end
210210+211211+ return 0
212212+end
213213+214214+# Since Fish completions are only loaded once the user triggers them, we trigger them ourselves
215215+# so we can properly delete any completions provided by another script.
216216+# Only do this if the program can be found, or else fish may print some errors; besides,
217217+# the existing completions will only be loaded if the program can be found.
218218+if type -q "storm"
219219+ # The space after the program name is essential to trigger completion for the program
220220+ # and not completion of the program name itself.
221221+ # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish.
222222+ complete --do-complete "storm " > /dev/null 2>&1
223223+end
224224+225225+# Remove any pre-existing completions for the program since we will be handling all of them.
226226+complete -c storm -e
227227+228228+# this will get called after the two calls below and clear the $__storm_perform_completion_once_result global
229229+complete -c storm -n '__storm_clear_perform_completion_once_result'
230230+# The call to __storm_prepare_completions will setup __storm_comp_results
231231+# which provides the program's completion choices.
232232+# If this doesn't require order preservation, we don't use the -k flag
233233+complete -c storm -n 'not __storm_requires_order_preservation && __storm_prepare_completions' -f -a '$__storm_comp_results'
234234+# otherwise we use the -k flag
235235+complete -k -c storm -n '__storm_requires_order_preservation && __storm_prepare_completions' -f -a '$__storm_comp_results'
+212
completions/storm.zsh
···11+#compdef storm
22+compdef _storm storm
33+44+# zsh completion for storm -*- shell-script -*-
55+66+__storm_debug()
77+{
88+ local file="$BASH_COMP_DEBUG_FILE"
99+ if [[ -n ${file} ]]; then
1010+ echo "$*" >> "${file}"
1111+ fi
1212+}
1313+1414+_storm()
1515+{
1616+ local shellCompDirectiveError=1
1717+ local shellCompDirectiveNoSpace=2
1818+ local shellCompDirectiveNoFileComp=4
1919+ local shellCompDirectiveFilterFileExt=8
2020+ local shellCompDirectiveFilterDirs=16
2121+ local shellCompDirectiveKeepOrder=32
2222+2323+ local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
2424+ local -a completions
2525+2626+ __storm_debug "\n========= starting completion logic =========="
2727+ __storm_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
2828+2929+ # The user could have moved the cursor backwards on the command-line.
3030+ # We need to trigger completion from the $CURRENT location, so we need
3131+ # to truncate the command-line ($words) up to the $CURRENT location.
3232+ # (We cannot use $CURSOR as its value does not work when a command is an alias.)
3333+ words=("${=words[1,CURRENT]}")
3434+ __storm_debug "Truncated words[*]: ${words[*]},"
3535+3636+ lastParam=${words[-1]}
3737+ lastChar=${lastParam[-1]}
3838+ __storm_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
3939+4040+ # For zsh, when completing a flag with an = (e.g., storm -n=<TAB>)
4141+ # completions must be prefixed with the flag
4242+ setopt local_options BASH_REMATCH
4343+ if [[ "${lastParam}" =~ '-.*=' ]]; then
4444+ # We are dealing with a flag with an =
4545+ flagPrefix="-P ${BASH_REMATCH}"
4646+ fi
4747+4848+ # Prepare the command to obtain completions
4949+ requestComp="${words[1]} __complete ${words[2,-1]}"
5050+ if [ "${lastChar}" = "" ]; then
5151+ # If the last parameter is complete (there is a space following it)
5252+ # We add an extra empty parameter so we can indicate this to the go completion code.
5353+ __storm_debug "Adding extra empty parameter"
5454+ requestComp="${requestComp} \"\""
5555+ fi
5656+5757+ __storm_debug "About to call: eval ${requestComp}"
5858+5959+ # Use eval to handle any environment variables and such
6060+ out=$(eval ${requestComp} 2>/dev/null)
6161+ __storm_debug "completion output: ${out}"
6262+6363+ # Extract the directive integer following a : from the last line
6464+ local lastLine
6565+ while IFS='\n' read -r line; do
6666+ lastLine=${line}
6767+ done < <(printf "%s\n" "${out[@]}")
6868+ __storm_debug "last line: ${lastLine}"
6969+7070+ if [ "${lastLine[1]}" = : ]; then
7171+ directive=${lastLine[2,-1]}
7272+ # Remove the directive including the : and the newline
7373+ local suffix
7474+ (( suffix=${#lastLine}+2))
7575+ out=${out[1,-$suffix]}
7676+ else
7777+ # There is no directive specified. Leave $out as is.
7878+ __storm_debug "No directive found. Setting do default"
7979+ directive=0
8080+ fi
8181+8282+ __storm_debug "directive: ${directive}"
8383+ __storm_debug "completions: ${out}"
8484+ __storm_debug "flagPrefix: ${flagPrefix}"
8585+8686+ if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
8787+ __storm_debug "Completion received error. Ignoring completions."
8888+ return
8989+ fi
9090+9191+ local activeHelpMarker="_activeHelp_ "
9292+ local endIndex=${#activeHelpMarker}
9393+ local startIndex=$((${#activeHelpMarker}+1))
9494+ local hasActiveHelp=0
9595+ while IFS='\n' read -r comp; do
9696+ # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
9797+ if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
9898+ __storm_debug "ActiveHelp found: $comp"
9999+ comp="${comp[$startIndex,-1]}"
100100+ if [ -n "$comp" ]; then
101101+ compadd -x "${comp}"
102102+ __storm_debug "ActiveHelp will need delimiter"
103103+ hasActiveHelp=1
104104+ fi
105105+106106+ continue
107107+ fi
108108+109109+ if [ -n "$comp" ]; then
110110+ # If requested, completions are returned with a description.
111111+ # The description is preceded by a TAB character.
112112+ # For zsh's _describe, we need to use a : instead of a TAB.
113113+ # We first need to escape any : as part of the completion itself.
114114+ comp=${comp//:/\\:}
115115+116116+ local tab="$(printf '\t')"
117117+ comp=${comp//$tab/:}
118118+119119+ __storm_debug "Adding completion: ${comp}"
120120+ completions+=${comp}
121121+ lastComp=$comp
122122+ fi
123123+ done < <(printf "%s\n" "${out[@]}")
124124+125125+ # Add a delimiter after the activeHelp statements, but only if:
126126+ # - there are completions following the activeHelp statements, or
127127+ # - file completion will be performed (so there will be choices after the activeHelp)
128128+ if [ $hasActiveHelp -eq 1 ]; then
129129+ if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
130130+ __storm_debug "Adding activeHelp delimiter"
131131+ compadd -x "--"
132132+ hasActiveHelp=0
133133+ fi
134134+ fi
135135+136136+ if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
137137+ __storm_debug "Activating nospace."
138138+ noSpace="-S ''"
139139+ fi
140140+141141+ if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
142142+ __storm_debug "Activating keep order."
143143+ keepOrder="-V"
144144+ fi
145145+146146+ if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
147147+ # File extension filtering
148148+ local filteringCmd
149149+ filteringCmd='_files'
150150+ for filter in ${completions[@]}; do
151151+ if [ ${filter[1]} != '*' ]; then
152152+ # zsh requires a glob pattern to do file filtering
153153+ filter="\*.$filter"
154154+ fi
155155+ filteringCmd+=" -g $filter"
156156+ done
157157+ filteringCmd+=" ${flagPrefix}"
158158+159159+ __storm_debug "File filtering command: $filteringCmd"
160160+ _arguments '*:filename:'"$filteringCmd"
161161+ elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
162162+ # File completion for directories only
163163+ local subdir
164164+ subdir="${completions[1]}"
165165+ if [ -n "$subdir" ]; then
166166+ __storm_debug "Listing directories in $subdir"
167167+ pushd "${subdir}" >/dev/null 2>&1
168168+ else
169169+ __storm_debug "Listing directories in ."
170170+ fi
171171+172172+ local result
173173+ _arguments '*:dirname:_files -/'" ${flagPrefix}"
174174+ result=$?
175175+ if [ -n "$subdir" ]; then
176176+ popd >/dev/null 2>&1
177177+ fi
178178+ return $result
179179+ else
180180+ __storm_debug "Calling _describe"
181181+ if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
182182+ __storm_debug "_describe found some completions"
183183+184184+ # Return the success of having called _describe
185185+ return 0
186186+ else
187187+ __storm_debug "_describe did not find completions."
188188+ __storm_debug "Checking if we should do file completion."
189189+ if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
190190+ __storm_debug "deactivating file completion"
191191+192192+ # We must return an error code here to let zsh know that there were no
193193+ # completions found by _describe; this is what will trigger other
194194+ # matching algorithms to attempt to find completions.
195195+ # For example zsh can match letters in the middle of words.
196196+ return 1
197197+ else
198198+ # Perform file completion
199199+ __storm_debug "Activating file completion"
200200+201201+ # We must return the result of this command, so it must be the
202202+ # last command, or else we must store its result to return it.
203203+ _arguments '*:filename:_files'" ${flagPrefix}"
204204+ fi
205205+ fi
206206+ fi
207207+}
208208+209209+# don't run the completion function when being source-ed or eval-ed
210210+if [ "$funcstack[1]" = "_storm" ]; then
211211+ _storm
212212+fi
+224
manpages/storm.1
···11+.TH STORM 1 "2025-11-09" "storm" "A Git-aware changelog manager for Go projects"
22+.SH NAME
33+storm - A Git-aware changelog manager for Go projects
44+.SH SYNOPSIS
55+\fBstorm\fP [\fIoptions\&.\&.\&.\fP] [\fIargument\&.\&.\&.\fP]
66+.SH DESCRIPTION
77+storm is a modern changelog generator inspired by Towncrier\&.
88+.PP
99+It manages \&.changes/ entries, generates Keep a Changelog sections,
1010+.PP
1111+and can review commits interactively through a TUI\&.
1212+.SH OPTIONS
1313+.TP
1414+\fB--o --output\fP
1515+Output changelog file path
1616+.TP
1717+\fB--repo\fP
1818+Path to the Git repository
1919+.SH COMMANDS
2020+.TP
2121+\fBbump\fP
2222+.RS 4
2323+Calculate the next semantic version and optionally update toolchain manifests
2424+.RE
2525+.TP
2626+\fBOPTIONS\fP
2727+.RS 4
2828+\fB--bump\fP
2929+Which semver component to bump (major, minor, or patch)
3030+.TP
3131+\fB--toolchain\fP
3232+Toolchain manifests to update (paths, types, or 'interactive')
3333+.RE
3434+.TP
3535+\fBcheck\fP [from] [to]
3636+.RS 4
3737+Validate changelog entries exist for all commits
3838+.RE
3939+.TP
4040+\fBOPTIONS\fP
4141+.RS 4
4242+\fB--since\fP
4343+Check changes since the given tag
4444+.RE
4545+.TP
4646+\fBcompletion\fP
4747+.RS 4
4848+Generate the autocompletion script for the specified shell
4949+.RE
5050+.TP
5151+\fBCOMMANDS\fP
5252+.RS 4
5353+\fBbash\fP
5454+.RS 4
5555+Generate the autocompletion script for bash
5656+.RE
5757+.TP
5858+\fBOPTIONS\fP
5959+.RS 4
6060+\fB--no-descriptions\fP
6161+disable completion descriptions
6262+.RE
6363+.TP
6464+\fBfish\fP
6565+.RS 4
6666+Generate the autocompletion script for fish
6767+.RE
6868+.TP
6969+\fBOPTIONS\fP
7070+.RS 4
7171+\fB--no-descriptions\fP
7272+disable completion descriptions
7373+.RE
7474+.TP
7575+\fBpowershell\fP
7676+.RS 4
7777+Generate the autocompletion script for powershell
7878+.RE
7979+.TP
8080+\fBOPTIONS\fP
8181+.RS 4
8282+\fB--no-descriptions\fP
8383+disable completion descriptions
8484+.RE
8585+.TP
8686+\fBzsh\fP
8787+.RS 4
8888+Generate the autocompletion script for zsh
8989+.RE
9090+.TP
9191+\fBOPTIONS\fP
9292+.RS 4
9393+\fB--no-descriptions\fP
9494+disable completion descriptions
9595+.RE
9696+.RE
9797+.TP
9898+\fBdiff\fP <from>\&.\&.<to> | diff <from> <to>
9999+.RS 4
100100+Show a line-based diff between two commits or tags
101101+.RE
102102+.TP
103103+\fBOPTIONS\fP
104104+.RS 4
105105+\fB--e --expanded\fP
106106+Show all unchanged lines (disable compression)
107107+.TP
108108+\fB--f --file\fP
109109+Specific file to diff (optional, shows all files if omitted)
110110+.TP
111111+\fB--v --view\fP
112112+Diff rendering: split or unified
113113+.RE
114114+.TP
115115+\fBgenerate\fP [from] [to]
116116+.RS 4
117117+Generate changelog entries from Git commits
118118+.RE
119119+.TP
120120+\fBOPTIONS\fP
121121+.RS 4
122122+\fB--i --interactive\fP
123123+Review changes interactively in a TUI
124124+.TP
125125+\fB--since\fP
126126+Generate changes since the given tag
127127+.RE
128128+.TP
129129+\fBhelp\fP [command]
130130+.RS 4
131131+Help about any command
132132+.RE
133133+.TP
134134+\fBrelease\fP
135135+.RS 4
136136+Promote unreleased changes into a new changelog version
137137+.RE
138138+.TP
139139+\fBOPTIONS\fP
140140+.RS 4
141141+\fB--bump\fP
142142+Automatically bump the previous version (major, minor, or patch)
143143+.TP
144144+\fB--clear-changes\fP
145145+Delete \&.changes/*\&.md files after successful release
146146+.TP
147147+\fB--date\fP
148148+Release date in YYYY-MM-DD format (default: today)
149149+.TP
150150+\fB--dry-run\fP
151151+Preview changes without writing files
152152+.TP
153153+\fB--tag\fP
154154+Create an annotated Git tag with release notes
155155+.TP
156156+\fB--toolchain\fP
157157+Toolchain manifests to update (paths, types, or 'interactive')
158158+.TP
159159+\fB--version\fP
160160+Semantic version for the new release (e\&.g\&., 1\&.3\&.0)
161161+.RE
162162+.TP
163163+\fBunreleased\fP
164164+.RS 4
165165+Manage unreleased changes (\&.changes directory)
166166+.RE
167167+.TP
168168+\fBCOMMANDS\fP
169169+.RS 4
170170+\fBadd\fP
171171+.RS 4
172172+Add a new unreleased change entry
173173+.RE
174174+.TP
175175+\fBOPTIONS\fP
176176+.RS 4
177177+\fB--scope\fP
178178+Optional scope or subsystem name
179179+.TP
180180+\fB--summary\fP
181181+Short summary of the change
182182+.TP
183183+\fB--type\fP
184184+Type of change (added, changed, fixed, removed, security)
185185+.RE
186186+.TP
187187+\fBlist\fP
188188+.RS 4
189189+List all unreleased changes
190190+.RE
191191+.TP
192192+\fBOPTIONS\fP
193193+.RS 4
194194+\fB--json\fP
195195+Output results as JSON
196196+.RE
197197+.TP
198198+\fBpartial\fP <commit-ref>
199199+.RS 4
200200+Create entry linked to a specific commit
201201+.RE
202202+.TP
203203+\fBOPTIONS\fP
204204+.RS 4
205205+\fB--scope\fP
206206+Optional scope or subsystem name
207207+.TP
208208+\fB--summary\fP
209209+Override summary (auto-detected from commit)
210210+.TP
211211+\fB--type\fP
212212+Override change type (auto-detected from commit)
213213+.RE
214214+.TP
215215+\fBreview\fP
216216+.RS 4
217217+Review unreleased changes interactively
218218+.RE
219219+.RE
220220+.TP
221221+\fBversion\fP
222222+.RS 4
223223+Print the current storm version
224224+.RE