Merge pull request 'feat(help): render --help from function headers; stop 8 functions executing on --help' (#132) from feat/header-driven-help into main
This commit was merged in pull request #132.
This commit is contained in:
@@ -0,0 +1,141 @@
|
|||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
# __fish_help_header <name> [args...]
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
# Prints <name>'s man-page comment header as a help menu on stdout.
|
||||||
|
# Intended as the first statement of a user-facing function's body:
|
||||||
|
#
|
||||||
|
# __fish_help_header (status current-function) $argv; and return 0
|
||||||
|
#
|
||||||
|
# Returns 1 -- printing nothing -- ONLY when args[1] is not a help flag.
|
||||||
|
# Every other outcome, including an unreadable or headerless source
|
||||||
|
# file, prints something and returns 0. That asymmetry is load-bearing:
|
||||||
|
# a return of 1 means "run the real body", and the real body of upgrade
|
||||||
|
# is `paru -Syu --noconfirm`. A parse failure must never return 1.
|
||||||
|
#
|
||||||
|
# Only args[1] is inspected, never the whole list. wake-lock, bkg,
|
||||||
|
# split and spwin take a command to run as their arguments, so
|
||||||
|
# scanning all of $argv would make `wake-lock rsync --help` print
|
||||||
|
# wake-lock's own help instead of running rsync.
|
||||||
|
#
|
||||||
|
# The header is read from the caller's source at call time rather than
|
||||||
|
# from the generated manual, so it cannot go stale between a header
|
||||||
|
# edit and a docs rebuild.
|
||||||
|
#
|
||||||
|
# ARGUMENTS
|
||||||
|
# name The calling function's name, from (status current-function)
|
||||||
|
# args... The caller's $argv, forwarded verbatim
|
||||||
|
#
|
||||||
|
# EXIT STATUS
|
||||||
|
# 0 Help was printed, including the degraded fallback
|
||||||
|
# 1 args[1] is not -h/--help; the caller should carry on
|
||||||
|
#
|
||||||
|
# EXAMPLE
|
||||||
|
# __fish_help_header (status current-function) $argv; and return 0
|
||||||
|
#
|
||||||
|
# NOTES
|
||||||
|
# Section labels are those of the manual SSOT parser in
|
||||||
|
# docs/manualtools.py. CATEGORY, COMPONENT and DEPENDENCIES are build
|
||||||
|
# metadata and are suppressed; SYNOPSIS renders as USAGE and EXAMPLE as
|
||||||
|
# EXAMPLES.
|
||||||
|
function __fish_help_header --argument-names name
|
||||||
|
# First argument only -- see DESCRIPTION.
|
||||||
|
contains -- "$argv[2]" -h --help; or return 1
|
||||||
|
|
||||||
|
set -l c_ttl (set_color --bold)
|
||||||
|
set -l c_sec (set_color --bold brblue)
|
||||||
|
set -l c_rst (set_color normal)
|
||||||
|
set -l miss " No documentation header found. Try: help config $name"
|
||||||
|
|
||||||
|
set -l file (functions -D -- $name 2>/dev/null)
|
||||||
|
if not test -f "$file"
|
||||||
|
# Quoted: set_color yields an EMPTY LIST under TERM=dumb, and an
|
||||||
|
# unquoted empty list in a concatenation annihilates the whole
|
||||||
|
# word -- the title line would silently vanish wherever colour is
|
||||||
|
# off, which is exactly where a test would be reading it.
|
||||||
|
echo "$c_ttl$name$c_rst"
|
||||||
|
echo $miss
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Collect the contiguous comment run directly above `function <name>`,
|
||||||
|
# walking backwards. This resolves multi-header files (fish-deps, gi,
|
||||||
|
# y) without reimplementing manualtools._block_identity, and is more
|
||||||
|
# accurate at runtime: in dops.fish it finds the header above
|
||||||
|
# `function docker` rather than attributing it to the file stem.
|
||||||
|
# One blank separator line is tolerated -- sponge_filter_secrets.fish
|
||||||
|
# is the only file that has one, and JOB-BRIEF-FINDINGS.md records it
|
||||||
|
# so this skip is not mistaken for dead code.
|
||||||
|
set -l lines (string split \n -- (command cat $file))
|
||||||
|
set -l pat '^\s*function\s+'(string escape --style=regex -- $name)'(\s|$)'
|
||||||
|
set -l start 0
|
||||||
|
for i in (seq (count $lines))
|
||||||
|
if string match -qr -- $pat $lines[$i]
|
||||||
|
set start $i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l header
|
||||||
|
if test $start -gt 1
|
||||||
|
set -l j (math $start - 1)
|
||||||
|
if test -z (string trim -- "$lines[$j]")
|
||||||
|
set j (math $j - 1)
|
||||||
|
end
|
||||||
|
while test $j -ge 1; and string match -q '#*' -- $lines[$j]
|
||||||
|
set -p header $lines[$j]
|
||||||
|
set j (math $j - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Render. Comment lines before the first `# LABEL` -- the copyright
|
||||||
|
# preamble -- carry no label and are dropped, matching
|
||||||
|
# manualtools._header_blocks.
|
||||||
|
set -l skip CATEGORY COMPONENT DEPENDENCIES
|
||||||
|
set -l label ""
|
||||||
|
set -l out
|
||||||
|
for line in $header
|
||||||
|
set -l m (string match -r -- '^#\s+([A-Z][A-Z ]*[A-Z])\s*$' $line)
|
||||||
|
if set -q m[2]
|
||||||
|
set label $m[2]
|
||||||
|
contains -- $label $skip; and continue
|
||||||
|
set -l shown (string replace SYNOPSIS USAGE -- $label)
|
||||||
|
set shown (string replace EXAMPLE EXAMPLES -- $shown)
|
||||||
|
# One blank line before a heading, never two: the header's own
|
||||||
|
# `#` separator has usually already emitted one.
|
||||||
|
if set -q out[1]; and test -n (string trim -- "$out[-1]")
|
||||||
|
set -a out ""
|
||||||
|
end
|
||||||
|
set -a out "$c_sec$shown$c_rst"
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
test -n "$label"; or continue
|
||||||
|
contains -- $label $skip; and continue
|
||||||
|
set -l body (string sub -s 2 -- $line)
|
||||||
|
if string match -q ' *' -- $body
|
||||||
|
set -a out " "(string sub -s 4 -- $body)
|
||||||
|
else
|
||||||
|
set -a out (string trim -- $body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Trim the trailing blank separator, mirroring
|
||||||
|
# manualtools._trailing_blanks.
|
||||||
|
while set -q out[-1]; and test -z (string trim -- "$out[-1]")
|
||||||
|
set -e out[-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "$c_ttl$name$c_rst"
|
||||||
|
if test (count $out) -eq 0
|
||||||
|
echo $miss
|
||||||
|
else
|
||||||
|
# out[1] is always a heading -- a body line cannot precede the
|
||||||
|
# first label -- so this blank is never doubled.
|
||||||
|
echo ""
|
||||||
|
printf '%s\n' $out
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
@@ -23,6 +23,8 @@
|
|||||||
# bd-pull myuser/myproject
|
# bd-pull myuser/myproject
|
||||||
# bd-pull rootiest/fish-config
|
# bd-pull rootiest/fish-config
|
||||||
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not set -q argv[1]; echo "Need repo owner/name"; return 1; end
|
if not set -q argv[1]; echo "Need repo owner/name"; return 1; end
|
||||||
if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end
|
if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# bkg firefox
|
# bkg firefox
|
||||||
function bkg --description 'Execute bkg'
|
function bkg --description 'Execute bkg'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Check if a command was provided as an argument.
|
# Check if a command was provided as an argument.
|
||||||
if test -z "$argv[1]"
|
if test -z "$argv[1]"
|
||||||
set -l c_head (set_color --bold cyan)
|
set -l c_head (set_color --bold cyan)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# branch feature/new-ui
|
# branch feature/new-ui
|
||||||
function branch --description 'Switch to or create a git branch'
|
function branch --description 'Switch to or create a git branch'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
if not git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
||||||
echo "Not a git repo."
|
echo "Not a git repo."
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -14,5 +14,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# check_fish_deps
|
# check_fish_deps
|
||||||
function check_fish_deps --description 'Check all fish-related dependencies'
|
function check_fish_deps --description 'Check all fish-related dependencies'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
fish-deps status
|
fish-deps status
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,5 +15,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# claude-docs
|
# claude-docs
|
||||||
function claude-docs --description 'Claude-code: Sync README with recent changes'
|
function claude-docs --description 'Claude-code: Sync README with recent changes'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
claude "Analyze the recent changes and update the README.md to ensure all features, setup instructions, and examples are 100% accurate. Prune any obsolete information."
|
claude "Analyze the recent changes and update the README.md to ensure all features, setup instructions, and examples are 100% accurate. Prune any obsolete information."
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,5 +15,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# claude-pr
|
# claude-pr
|
||||||
function claude-pr --description 'Claude-code: New branch, commit, push, and PR'
|
function claude-pr --description 'Claude-code: New branch, commit, push, and PR'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
claude "Act as a senior engineer. Execute this sequence: 1. Create a new git branch (kebab-case). 2. Stage changes and write a Conventional Commit message. 3. Self-verify the changes by running relevant build/test commands or linting. 4. Push to remote. 5. Create a PR to 'main' including a summary of changes and a 'Manual Verification' section containing a Markdown checklist (- [ ]) of specific, bite-sized steps required to manually verify the functionality."
|
claude "Act as a senior engineer. Execute this sequence: 1. Create a new git branch (kebab-case). 2. Stage changes and write a Conventional Commit message. 3. Self-verify the changes by running relevant build/test commands or linting. 4. Push to remote. 5. Create a PR to 'main' including a summary of changes and a 'Manual Verification' section containing a Markdown checklist (- [ ]) of specific, bite-sized steps required to manually verify the functionality."
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# cleanup
|
# cleanup
|
||||||
function cleanup --description 'Log orphans to ~/.removed_orphans and remove them'
|
function cleanup --description 'Log orphans to ~/.removed_orphans and remove them'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
set -l orphans (pacman -Qtdq)
|
set -l orphans (pacman -Qtdq)
|
||||||
if test -n "$orphans"
|
if test -n "$orphans"
|
||||||
echo "📝 Logging orphans to ~/.removed_orphans..."
|
echo "📝 Logging orphans to ~/.removed_orphans..."
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# fast
|
# fast
|
||||||
function fast --description 'Placeholder for future fast utility'
|
function fast --description 'Placeholder for future fast utility'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# ANSI Escape Codes (Standard 16-color palette)
|
# ANSI Escape Codes (Standard 16-color palette)
|
||||||
set -l bold "\e[1m"
|
set -l bold "\e[1m"
|
||||||
set -l italic "\e[3m"
|
set -l italic "\e[3m"
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
# fc
|
# fc
|
||||||
# fc git
|
# fc git
|
||||||
function fc --description 'Edit and execute the last command (Bash-style fc)'
|
function fc --description 'Edit and execute the last command (Bash-style fc)'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
set -l tmpfile (mktemp /tmp/fish_fc.XXXXXX).fish
|
set -l tmpfile (mktemp /tmp/fish_fc.XXXXXX).fish
|
||||||
|
|
||||||
if count $argv >/dev/null
|
if count $argv >/dev/null
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ function fish-deps --description 'Manage fish shell dependencies'
|
|||||||
_fish_deps_status
|
_fish_deps_status
|
||||||
case install
|
case install
|
||||||
_fish_deps_install $flags
|
_fish_deps_install $flags
|
||||||
|
case -h --help
|
||||||
|
# Reuse the existing menu rather than the header renderer: it
|
||||||
|
# is richer, and it is already the text the unknown-subcommand
|
||||||
|
# path prints. Previously --help fell into `case '*'` and
|
||||||
|
# exited 1 with "Unknown subcommand: --help".
|
||||||
|
__fish_deps_help
|
||||||
|
return 0
|
||||||
case update
|
case update
|
||||||
_fish_deps_update
|
_fish_deps_update
|
||||||
case sync
|
case sync
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# fzf-update
|
# fzf-update
|
||||||
function fzf-update --description 'Install or upgrade fzf from git HEAD'
|
function fzf-update --description 'Install or upgrade fzf from git HEAD'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if test -d ~/.fzf
|
if test -d ~/.fzf
|
||||||
echo "Updating fzf..."
|
echo "Updating fzf..."
|
||||||
git -C ~/.fzf pull --ff-only
|
git -C ~/.fzf pull --ff-only
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# gip
|
# gip
|
||||||
function gip --description 'Show all public IP addresses'
|
function gip --description 'Show all public IP addresses'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
echo -n "IPv4: "
|
echo -n "IPv4: "
|
||||||
curl -4 -s --max-time 2 https://icanhazip.com || echo "Not detected"
|
curl -4 -s --max-time 2 https://icanhazip.com || echo "Not detected"
|
||||||
echo -n "IPv6: "
|
echo -n "IPv6: "
|
||||||
|
|||||||
@@ -13,5 +13,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# gip4
|
# gip4
|
||||||
function gip4 --wraps='curl' --description 'Get public IPv4 address'
|
function gip4 --wraps='curl' --description 'Get public IPv4 address'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
curl -4 -s https://icanhazip.com
|
curl -4 -s https://icanhazip.com
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# gip6
|
# gip6
|
||||||
function gip6 --description 'Get public IPv6 address'
|
function gip6 --description 'Get public IPv6 address'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Use -6 to force IPv6 and --fail to catch network errors
|
# Use -6 to force IPv6 and --fail to catch network errors
|
||||||
set -l ip (curl -6 -s --fail https://icanhazip.com 2>/dev/null)
|
set -l ip (curl -6 -s --fail https://icanhazip.com 2>/dev/null)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# hist
|
# hist
|
||||||
function hist --description 'Search fish history and put it in the prompt'
|
function hist --description 'Search fish history and put it in the prompt'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Opinionated guard (C4): integrations disabled
|
# Opinionated guard (C4): integrations disabled
|
||||||
if not __fish_config_op_enabled (status current-function)
|
if not __fish_config_op_enabled (status current-function)
|
||||||
set -l c_err (set_color red)
|
set -l c_err (set_color red)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lD ~/projects
|
# lD ~/projects
|
||||||
function lD --description 'List directories only'
|
function lD --description 'List directories only'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --only-dirs --long --icons --color=auto --hyperlink $argv
|
eza --only-dirs --long --icons --color=auto --hyperlink $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# ld
|
# ld
|
||||||
function ld --description 'Run lazydocker on the current Docker context'
|
function ld --description 'Run lazydocker on the current Docker context'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not type -q docker
|
if not type -q docker
|
||||||
echo "ld: docker is not installed" >&2
|
echo "ld: docker is not installed" >&2
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# limine-edit
|
# limine-edit
|
||||||
function limine-edit --description 'Safely edit and re-verify Limine configuration'
|
function limine-edit --description 'Safely edit and re-verify Limine configuration'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# 1. Open the config with sudoedit
|
# 1. Open the config with sudoedit
|
||||||
sudoedit /boot/limine.conf
|
sudoedit /boot/limine.conf
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lock
|
# lock
|
||||||
function lock --wraps='loginctl' --description 'alias lock=loginctl'
|
function lock --wraps='loginctl' --description 'alias lock=loginctl'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
loginctl lock-session
|
loginctl lock-session
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lsr ~/projects
|
# lsr ~/projects
|
||||||
function lsr --description 'Reversed time-sorted listing'
|
function lsr --description 'Reversed time-sorted listing'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --oneline --sort=modified --reverse --icons --color=auto --hyperlink $argv
|
eza --oneline --sort=modified --reverse --icons --color=auto --hyperlink $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lss ~/downloads
|
# lss ~/downloads
|
||||||
function lss --description 'Size-sorted listing'
|
function lss --description 'Size-sorted listing'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --oneline --long --all --sort=size --icons --color=auto --hyperlink --color-scale=size --color-scale-mode=gradient $argv
|
eza --oneline --long --all --sort=size --icons --color=auto --hyperlink --color-scale=size --color-scale-mode=gradient $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lstree ~/projects/myapp
|
# lstree ~/projects/myapp
|
||||||
function lstree --description 'Full recursive tree listing'
|
function lstree --description 'Full recursive tree listing'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --tree --icons --color=auto --hyperlink=auto $argv
|
eza --tree --icons --color=auto --hyperlink=auto $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lt ~/projects
|
# lt ~/projects
|
||||||
function lt --description 'Tree listing, depth 2'
|
function lt --description 'Tree listing, depth 2'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --tree --level=2 --icons --color=auto --hyperlink $argv
|
eza --tree --level=2 --icons --color=auto --hyperlink $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# ltr ~/projects
|
# ltr ~/projects
|
||||||
function ltr --description 'Reversed time-sorted listing'
|
function ltr --description 'Reversed time-sorted listing'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --long --all --sort=modified --icons --hyperlink --color=auto --color-scale=age --color-scale-mode=gradient $argv
|
eza --long --all --sort=modified --icons --hyperlink --color=auto --color-scale=age --color-scale-mode=gradient $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# lx ~/projects
|
# lx ~/projects
|
||||||
function lx --description 'Extension-sorted listing'
|
function lx --description 'Extension-sorted listing'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if which eza >/dev/null 2>&1
|
if which eza >/dev/null 2>&1
|
||||||
eza --long --all --sort=extension --icons --color=auto --hyperlink $argv
|
eza --long --all --sort=extension --icons --color=auto --hyperlink $argv
|
||||||
else if which lsd >/dev/null 2>&1
|
else if which lsd >/dev/null 2>&1
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# parur
|
# parur
|
||||||
function parur --description 'Interactively search and remove an installed package using fzf'
|
function parur --description 'Interactively search and remove an installed package using fzf'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
set -l aur ""
|
set -l aur ""
|
||||||
if type -q paru
|
if type -q paru
|
||||||
set aur paru
|
set aur paru
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# poke ~/projects/new/src/main.fish
|
# poke ~/projects/new/src/main.fish
|
||||||
function poke --description 'touch with automatic parent directory creation'
|
function poke --description 'touch with automatic parent directory creation'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if test (count $argv) -eq 0
|
if test (count $argv) -eq 0
|
||||||
echo (set_color red)"poke: no file specified"(set_color normal) >&2
|
echo (set_color red)"poke: no file specified"(set_color normal) >&2
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -14,5 +14,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# ports
|
# ports
|
||||||
function ports --wraps='sudo' --description 'Show active network listeners'
|
function ports --wraps='sudo' --description 'Show active network listeners'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
sudo lsof -iTCP -sTCP:LISTEN -P -n
|
sudo lsof -iTCP -sTCP:LISTEN -P -n
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# qr "https://example.com"
|
# qr "https://example.com"
|
||||||
# echo "hello" | qr
|
# echo "hello" | qr
|
||||||
function qr --description 'Generate a QR code from text or pipe'
|
function qr --description 'Generate a QR code from text or pipe'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if type -q qrencode
|
if type -q qrencode
|
||||||
if set -q argv[1]
|
if set -q argv[1]
|
||||||
echo $argv | qrencode -t utf8
|
echo $argv | qrencode -t utf8
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
# sbver
|
# sbver
|
||||||
# sbver --brief
|
# sbver --brief
|
||||||
function sbver --description 'Verifies Secure Boot status of EFI binaries using sbctl'
|
function sbver --description 'Verifies Secure Boot status of EFI binaries using sbctl'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not type -q sbctl
|
if not type -q sbctl
|
||||||
echo "Error: 'sbctl' is not installed."
|
echo "Error: 'sbctl' is not installed."
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# screensleep
|
# screensleep
|
||||||
function screensleep --description 'Turn off the display using KDE PowerDevil'
|
function screensleep --description 'Turn off the display using KDE PowerDevil'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Optional: 1-second delay to ensure no keystrokes wake it immediately
|
# Optional: 1-second delay to ensure no keystrokes wake it immediately
|
||||||
sleep 1
|
sleep 1
|
||||||
busctl --user call \
|
busctl --user call \
|
||||||
|
|||||||
@@ -29,6 +29,10 @@
|
|||||||
# split
|
# split
|
||||||
# split -v nvim README.md
|
# split -v nvim README.md
|
||||||
function split --description 'Run a command in a new terminal split'
|
function split --description 'Run a command in a new terminal split'
|
||||||
|
# -h is --horizontal here (see this function's own ARGUMENTS),
|
||||||
|
# so only the long form may reach the renderer.
|
||||||
|
test "$argv[1]" = --help; and __fish_help_header (status current-function) --help; and return 0
|
||||||
|
|
||||||
# Opinionated guard (C4): integrations disabled
|
# Opinionated guard (C4): integrations disabled
|
||||||
if not __fish_config_op_enabled (status current-function)
|
if not __fish_config_op_enabled (status current-function)
|
||||||
set -l c_err (set_color red)
|
set -l c_err (set_color red)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# spwin
|
# spwin
|
||||||
function spwin --wraps='~/.config/kitty/spawn-window.sh' --description 'spawn window in kitty or wezterm'
|
function spwin --wraps='~/.config/kitty/spawn-window.sh' --description 'spawn window in kitty or wezterm'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Opinionated guard (C4): integrations disabled
|
# Opinionated guard (C4): integrations disabled
|
||||||
if not __fish_config_op_enabled (status current-function)
|
if not __fish_config_op_enabled (status current-function)
|
||||||
set -l c_err (set_color red)
|
set -l c_err (set_color red)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# steam-dl
|
# steam-dl
|
||||||
function steam-dl --description 'Run Steam while inhibiting system sleep'
|
function steam-dl --description 'Run Steam while inhibiting system sleep'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
echo "Inhibiting sleep while Steam downloads..."
|
echo "Inhibiting sleep while Steam downloads..."
|
||||||
systemd-inhibit --why="Active Download" --who="User" --what=idle:sleep steam
|
systemd-inhibit --why="Active Download" --who="User" --what=idle:sleep steam
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# sudo-toggle
|
# sudo-toggle
|
||||||
function sudo-toggle --description 'Toggle sudo password requirement on/off'
|
function sudo-toggle --description 'Toggle sudo password requirement on/off'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Check the file size using sudo stat to see if our bypass rule is active
|
# Check the file size using sudo stat to see if our bypass rule is active
|
||||||
set -l file_size (sudo stat -c %s /etc/sudoers.d/nofail-toggle 2>/dev/null)
|
set -l file_size (sudo stat -c %s /etc/sudoers.d/nofail-toggle 2>/dev/null)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# swapstat
|
# swapstat
|
||||||
function swapstat --description 'View colorized zRAM and swappiness status'
|
function swapstat --description 'View colorized zRAM and swappiness status'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
set -l swappiness (sysctl -n vm.swappiness)
|
set -l swappiness (sysctl -n vm.swappiness)
|
||||||
set -l zdata (zramctl --bytes --noheadings --output DATA,TOTAL /dev/zram0 2>/dev/null)
|
set -l zdata (zramctl --bytes --noheadings --output DATA,TOTAL /dev/zram0 2>/dev/null)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# tab
|
# tab
|
||||||
function tab --description 'Spawn a new tab in the current terminal'
|
function tab --description 'Spawn a new tab in the current terminal'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Opinionated guard (C4): integrations disabled
|
# Opinionated guard (C4): integrations disabled
|
||||||
if not __fish_config_op_enabled (status current-function)
|
if not __fish_config_op_enabled (status current-function)
|
||||||
set -l c_err (set_color red)
|
set -l c_err (set_color red)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# tmux-clean
|
# tmux-clean
|
||||||
function tmux-clean --description 'Kill all tmux sessions except the current one'
|
function tmux-clean --description 'Kill all tmux sessions except the current one'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Get a list of all session names that are NOT currently attached
|
# Get a list of all session names that are NOT currently attached
|
||||||
set sessions (tmux list-sessions -F '#{session_name} #{session_attached}' | string match -rv ' 1$' | string split -f1 ' ')
|
set sessions (tmux list-sessions -F '#{session_name} #{session_attached}' | string match -rv ' 1$' | string split -f1 ' ')
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# upgrade
|
# upgrade
|
||||||
function upgrade --description 'Full system upgrade via paru or yay'
|
function upgrade --description 'Full system upgrade via paru or yay'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# Opinionated guard (C4): integrations disabled
|
# Opinionated guard (C4): integrations disabled
|
||||||
if not __fish_config_op_enabled (status current-function)
|
if not __fish_config_op_enabled (status current-function)
|
||||||
set -l c_err (set_color red)
|
set -l c_err (set_color red)
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# wake-lock rsync -avz src/ dest/
|
# wake-lock rsync -avz src/ dest/
|
||||||
function wake-lock --description 'Run a command while inhibiting system sleep'
|
function wake-lock --description 'Run a command while inhibiting system sleep'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if test (count $argv) -eq 0
|
if test (count $argv) -eq 0
|
||||||
set -l c_head (set_color --bold cyan)
|
set -l c_head (set_color --bold cyan)
|
||||||
set -l c_cmd (set_color --bold)
|
set -l c_cmd (set_color --bold)
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
# Copyright (C) 2026 Rootiest
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# Coverage for the header-driven --help renderer (__fish_help_header) and
|
||||||
|
# the repo-wide rule that every user-facing function handles -h/--help.
|
||||||
|
#
|
||||||
|
# Runs isolated (no `# MODE:` marker): every case spawns its own --no-config
|
||||||
|
# fish with an explicit fish_function_path, so none of it needs a loaded
|
||||||
|
# session -- only $repo_root/functions (or a throwaway fixture dir) on the
|
||||||
|
# child's function path.
|
||||||
|
|
||||||
|
source (realpath (dirname (status filename)))/lib.fish
|
||||||
|
|
||||||
|
# Helper: run `<fn> $argv` in a throwaway fish that can see both $dir and
|
||||||
|
# this repo's real functions/, so a fixture function can call the real
|
||||||
|
# __fish_help_header. $dir is always mktemp -d output, never spaced.
|
||||||
|
function _help_probe --argument-names dir
|
||||||
|
env TERM=dumb fish --no-config -c \
|
||||||
|
"set -g fish_function_path $dir $repo_root/functions; $argv[2..]"
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_help_renderer
|
||||||
|
set -l tmp (mktemp -d)
|
||||||
|
printf '%s\n' \
|
||||||
|
'# Copyright (C) 2026 Rootiest' \
|
||||||
|
'' \
|
||||||
|
'# CATEGORY' \
|
||||||
|
'# 99-fixture' \
|
||||||
|
'#' \
|
||||||
|
'# SYNOPSIS' \
|
||||||
|
'# fixturefn [options]' \
|
||||||
|
'#' \
|
||||||
|
'# DESCRIPTION' \
|
||||||
|
'# First paragraph.' \
|
||||||
|
'#' \
|
||||||
|
'# Second paragraph.' \
|
||||||
|
'#' \
|
||||||
|
'# ARGUMENTS' \
|
||||||
|
'# -x Do the thing' \
|
||||||
|
'# more Indented continuation' \
|
||||||
|
'#' \
|
||||||
|
'# EXAMPLE' \
|
||||||
|
'# fixturefn -x' \
|
||||||
|
'function fixturefn' \
|
||||||
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
|
' echo RAN-BODY' \
|
||||||
|
'end' >$tmp/fixturefn.fish
|
||||||
|
|
||||||
|
set -l out (_help_probe $tmp 'fixturefn --help')
|
||||||
|
set -l code $status
|
||||||
|
set -l text (string join \n $out)
|
||||||
|
rm -rf $tmp
|
||||||
|
|
||||||
|
set -l failed 0
|
||||||
|
if test $code -ne 0
|
||||||
|
echo " renderer exited $code, expected 0"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if contains -- RAN-BODY $out
|
||||||
|
echo " body executed despite --help"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if not contains -- USAGE $out
|
||||||
|
echo " missing USAGE heading (SYNOPSIS should render as USAGE)"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if contains -- CATEGORY $out
|
||||||
|
echo " CATEGORY leaked into the menu"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if not string match -q '* more Indented continuation*' -- $text
|
||||||
|
echo " nested ARGUMENTS indentation lost"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
# Index-based, not a glob: fish's `string match` glob `*` does not
|
||||||
|
# span newlines, so a pattern straddling two lines silently never
|
||||||
|
# matches and the assertion would pass for the wrong reason.
|
||||||
|
set -l i (contains -i -- " First paragraph." $out)
|
||||||
|
if test -z "$i"
|
||||||
|
echo " DESCRIPTION body missing entirely"
|
||||||
|
set failed 1
|
||||||
|
else
|
||||||
|
# Indices hoisted: a command substitution inside a quoted index
|
||||||
|
# ("$out[(math ...)]") is a fish parse error, not an expansion.
|
||||||
|
set -l gap (math $i + 1)
|
||||||
|
set -l nxt (math $i + 2)
|
||||||
|
if test -n "$out[$gap]"
|
||||||
|
echo " multi-paragraph DESCRIPTION lost its blank line"
|
||||||
|
set failed 1
|
||||||
|
else if test "$out[$nxt]" != " Second paragraph."
|
||||||
|
echo " second paragraph missing after the blank"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
test $failed -eq 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_help_renderer_degrades_safely
|
||||||
|
# The renderer must return 1 ONLY when argv[1] is not a help flag.
|
||||||
|
# A missing or label-less header must still print and exit 0, because
|
||||||
|
# returning 1 hands control back to the caller's body -- and the body
|
||||||
|
# of upgrade(1) is `paru -Syu --noconfirm`.
|
||||||
|
set -l tmp (mktemp -d)
|
||||||
|
printf '%s\n' \
|
||||||
|
'function headerless' \
|
||||||
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
|
" touch $tmp/BODY-RAN" \
|
||||||
|
'end' >$tmp/headerless.fish
|
||||||
|
# A comment run carrying no `# LABEL` line at all.
|
||||||
|
printf '%s\n' \
|
||||||
|
'# just an ordinary comment, no labels here' \
|
||||||
|
'function malformed' \
|
||||||
|
' __fish_help_header (status current-function) $argv; and return 0' \
|
||||||
|
" touch $tmp/BODY-RAN" \
|
||||||
|
'end' >$tmp/malformed.fish
|
||||||
|
|
||||||
|
set -l failed 0
|
||||||
|
for fn in headerless malformed
|
||||||
|
set -l out (_help_probe $tmp "$fn --help")
|
||||||
|
set -l code $status
|
||||||
|
if test $code -ne 0
|
||||||
|
echo " $fn --help exited $code, expected 0"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if test (count $out) -eq 0
|
||||||
|
echo " $fn --help printed nothing"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if not contains -- $fn $out
|
||||||
|
echo " $fn --help did not name the function"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if test -e $tmp/BODY-RAN
|
||||||
|
echo " $fn executed its body despite --help"
|
||||||
|
set failed 1
|
||||||
|
rm -f $tmp/BODY-RAN
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The inverse: no help flag must return 1 and let the body run.
|
||||||
|
_help_probe $tmp headerless >/dev/null 2>&1
|
||||||
|
if not test -e $tmp/BODY-RAN
|
||||||
|
echo " body did NOT run when no help flag was passed"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
|
||||||
|
rm -rf $tmp
|
||||||
|
# Explicit, never a trailing `if`: standing gotcha #5 -- an if with no
|
||||||
|
# branch taken resolves $status to 0 and the test would pass silently.
|
||||||
|
test $failed -eq 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function test_help_never_executes_destructive_path
|
||||||
|
# These eight ignore $argv entirely, so before the header-driven help
|
||||||
|
# landed, `upgrade --help` ran `paru -Syu --noconfirm`. The check has
|
||||||
|
# to prove --help does NOT reach the destructive path *without* ever
|
||||||
|
# running it: every external binary the eight can reach is shadowed by
|
||||||
|
# a recording stub on PATH, and the recorder must stay empty.
|
||||||
|
#
|
||||||
|
# WARNING: a silent pass here means a MISSING STUB, not success. If a
|
||||||
|
# function shows neither an EXECUTED line nor its own help, its
|
||||||
|
# command is absent from the stub list below -- add it. A test that
|
||||||
|
# cannot fail proves nothing about a body that runs sudo pacman -Rns.
|
||||||
|
set -l tmp (mktemp -d)
|
||||||
|
mkdir -p $tmp/bin
|
||||||
|
set -l log $tmp/invoked.log
|
||||||
|
touch $log
|
||||||
|
|
||||||
|
for b in sudo pacman paru yay loginctl busctl tmux systemd-inhibit \
|
||||||
|
sudoedit limine-enroll-config limine-mkinitcpio sbctl git fzf steam
|
||||||
|
printf '#!/bin/sh\necho "$(basename "$0") $*" >> %s\n' $log >$tmp/bin/$b
|
||||||
|
chmod +x $tmp/bin/$b
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l failed 0
|
||||||
|
for fn in cleanup fzf-update limine-edit lock screensleep sudo-toggle \
|
||||||
|
tmux-clean upgrade
|
||||||
|
set -l out (env TERM=dumb PATH="$tmp/bin:$PATH" HOME=$tmp \
|
||||||
|
fish --no-config -c \
|
||||||
|
"set -g fish_function_path $repo_root/functions; $fn --help" 2>/dev/null)
|
||||||
|
set -l code $status
|
||||||
|
|
||||||
|
if test $code -ne 0
|
||||||
|
echo " $fn --help exited $code, expected 0"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
if not contains -- $fn $out
|
||||||
|
echo " $fn --help did not print its own help"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
set -l ran (string trim -- (command cat $log))
|
||||||
|
if test -n "$ran"
|
||||||
|
echo " $fn --help EXECUTED: $ran"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
echo -n "" >$log
|
||||||
|
end
|
||||||
|
|
||||||
|
rm -rf $tmp
|
||||||
|
test $failed -eq 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Functions published in the manual that are exempt from the -h/--help
|
||||||
|
# rule. Rationale per entry: AGENTS/specs/2026-09-07-header-driven-help-design.md
|
||||||
|
# §4. This array is the ONLY machine-readable copy of the exempt set.
|
||||||
|
#
|
||||||
|
# EXEMPT-A -- shadows a same-named binary, or forwards $argv to one named
|
||||||
|
# tool that owns its own --help. Intercepting would hide that tool's help,
|
||||||
|
# and for the C1-guarded shadows it also breaks the disabled-fallback
|
||||||
|
# contract, where the bare tool is supposed to answer.
|
||||||
|
set -g __help_exempt \
|
||||||
|
agy antigravity-ide bash cat cdi cffetch cheat claude clone clonet \
|
||||||
|
config-toggle copy docker du dusize fast-cli ffetch gitui gitup jr \
|
||||||
|
joplin less ls mkdir mv paste ping rawfish rg rm search ssh top \
|
||||||
|
view yt-dlp
|
||||||
|
# EXEMPT-B -- invoked by fish, never typed by a user.
|
||||||
|
set -a __help_exempt fish_prompt fish_right_prompt fish_mode_prompt \
|
||||||
|
sponge_filter_secrets
|
||||||
|
|
||||||
|
function test_every_user_facing_function_has_help
|
||||||
|
set -l failed 0
|
||||||
|
set -l published
|
||||||
|
|
||||||
|
for f in $repo_root/functions/*.fish
|
||||||
|
set -l lines (string split \n -- (command cat $f))
|
||||||
|
# Published == carries a `# CATEGORY` block, matching
|
||||||
|
# manualtools.parse_functions.
|
||||||
|
contains -- "# CATEGORY" (string trim -- $lines); or continue
|
||||||
|
# Resolve the real defined name; the file stem can disagree with it
|
||||||
|
# (formerly dops.fish defined `docker` -- see JOB-BRIEF-FINDINGS.md
|
||||||
|
# §1, fixed by splitting it into dops.fish and docker.fish).
|
||||||
|
set -l name (string match -rg '^\s*function\s+(\S+)' -- $lines)[1]
|
||||||
|
test -n "$name"; or continue
|
||||||
|
set name (string trim -c "'\"" -- $name)
|
||||||
|
string match -q '_*' -- $name; and continue
|
||||||
|
set -a published $name
|
||||||
|
|
||||||
|
contains -- $name $__help_exempt; and continue
|
||||||
|
|
||||||
|
# Body == everything from the `function` line down, comment lines
|
||||||
|
# dropped, so a header that merely mentions --help cannot pass.
|
||||||
|
set -l body
|
||||||
|
set -l in_body 0
|
||||||
|
for l in $lines
|
||||||
|
test $in_body -eq 1; or string match -qr '^\s*function\s' -- $l; and set in_body 1
|
||||||
|
test $in_body -eq 1; or continue
|
||||||
|
string match -qr '^\s*#' -- $l; and continue
|
||||||
|
set -a body $l
|
||||||
|
end
|
||||||
|
if not string match -qr -- '__fish_help_header|_flag_help|h/help|--help' \
|
||||||
|
(string join \n -- $body)
|
||||||
|
echo " $name: no -h/--help handling and not in \$__help_exempt"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Guard against a stale exempt list: every exempt name must still be a
|
||||||
|
# published function. Catches renames and deletions.
|
||||||
|
for e in $__help_exempt
|
||||||
|
if not contains -- $e $published
|
||||||
|
echo " \$__help_exempt lists '$e', which is no longer published"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test $failed -eq 0
|
||||||
|
end
|
||||||
|
|
||||||
|
section "help: renderer"
|
||||||
|
check "full render: headings, indentation, multi-paragraph description" true (test_help_renderer; and echo true; or echo false)
|
||||||
|
|
||||||
|
section "help: renderer degrades safely"
|
||||||
|
check "headerless/malformed functions still print and exit 0" true (test_help_renderer_degrades_safely; and echo true; or echo false)
|
||||||
|
|
||||||
|
section "help: destructive paths"
|
||||||
|
check "eight functions never execute their destructive path on --help" true (test_help_never_executes_destructive_path; and echo true; or echo false)
|
||||||
|
|
||||||
|
section "help: coverage"
|
||||||
|
check "every user-facing function has --help or is exempt" true (test_every_user_facing_function_has_help; and echo true; or echo false)
|
||||||
|
|
||||||
|
report
|
||||||
Reference in New Issue
Block a user