diff --git a/bash-helper.sh b/bash-helper.sh index d482f76..d7f01c6 100755 --- a/bash-helper.sh +++ b/bash-helper.sh @@ -628,21 +628,116 @@ colorize_syntax() { printf "%s" "$colored_syntax" } -# Extract all flags from man page or help +# Extract flags with descriptions from man pages +# New enhanced version that includes what each flag does extract_all_flags() { local cmd_name="$1" - local flags_list="" + local flags_output="" if type "$cmd_name" 2>/dev/null | grep -q "shell builtin"; then - # Shell builtin - use help command - flags_list=$(help "$cmd_name" 2>/dev/null | grep -E '^\s*-') + # Shell builtin - use help command (basic format) + flags_output=$(help "$cmd_name" 2>/dev/null | grep -E '^\s*-') elif command -v "$cmd_name" &> /dev/null; then - # External command - use man page - # Get lines that start with optional whitespace and a dash (flags) - flags_list=$(man "$cmd_name" 2>/dev/null | col -b | grep -E '^[[:space:]]*-' | head -200) + # External command - extract from man page OPTIONS or DESCRIPTION section with descriptions + local man_content=$(man "$cmd_name" 2>/dev/null | col -b) + + # Try OPTIONS section first, then DESCRIPTION (GNU coreutils use DESCRIPTION) + # Exclude the section headers themselves to prevent early AWK exit + local options_section=$(echo "$man_content" | sed -n '/^OPTIONS/,/^[A-Z][A-Z]/{/^OPTIONS/d; /^[A-Z][A-Z]/d; p;}' | head -300) + + if [ -z "$options_section" ]; then + # Try DESCRIPTION section instead + options_section=$(echo "$man_content" | sed -n '/^DESCRIPTION/,/^[A-Z][A-Z]/{/^DESCRIPTION/d; /^[A-Z][A-Z]/d; p;}' | head -300) + fi + + if [ -n "$options_section" ]; then + # Parse flags with their descriptions using portable AWK + # Man pages have two formats: + # 1. " --help Output..." (description on same line) + # 2. " -a, --all\n description..." (description on next line, deeply indented) + flags_output=$(echo "$options_section" | awk ' + BEGIN { flag=""; desc=""; } + + /^[[:space:]]*-/ { + # Print previous flag if exists + if (flag != "") { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", desc) + if (desc != "") { + printf "%s — %s\n", flag, desc + } else { + printf "%s\n", flag + } + } + + # New flag line - extract flag and check for inline description + line = $0 + gsub(/^[[:space:]]+/, "", line) # Remove leading whitespace + + # Check if there is content after the flag (separated by multiple spaces/tabs) + # Flag pattern: starts with -, may have comma and long form + # Look for 2+ spaces/tabs separating flag from description + if (match(line, /^[^[:space:]]+([[:space:]]+[^[:space:]]+)?[[:space:]][[:space:]]+/)) { + # Description on same line + flag_end = RSTART + RLENGTH - 1 + flag = substr(line, 1, flag_end) + gsub(/[[:space:]]+$/, "", flag) # Trim trailing spaces + desc = substr(line, flag_end + 1) + gsub(/^[[:space:]]+/, "", desc) # Trim leading spaces from description + } else { + # No inline description, will be on next line(s) + flag = line + desc = "" + } + next + } + + /^[[:space:]]+[^-]/ { + # Description continuation line (indented, no leading dash) + if (flag != "") { + line = $0 + gsub(/^[[:space:]]+|[[:space:]]+$/, "", line) + if (desc == "") { + desc = line + } else { + desc = desc " " line + } + } + } + + /^[A-Z][A-Z]/ { + # Major section header - end of options + if (flag != "") { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", desc) + if (desc != "") { + printf "%s — %s\n", flag, desc + } else { + printf "%s\n", flag + } + } + exit + } + + END { + # Print last flag + if (flag != "") { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", desc) + if (desc != "") { + printf "%s — %s\n", flag, desc + } else { + printf "%s\n", flag + } + } + } + ' | head -150) + fi + + # Fallback: if no OPTIONS section, just get flag lines + if [ -z "$flags_output" ]; then + flags_output=$(echo "$man_content" | grep -E '^[[:space:]]*-' | head -100) + fi fi - echo "$flags_list" + echo "$flags_output" } # Interactive flag browser with fuzzy search @@ -1018,22 +1113,22 @@ if [ "$INTERACTIVE" = true ]; then DESC_LEN=${#DESC_PART} SYNTAX_LEN=${#SYNTAX_PART} # Use original length, not colorized - # Calculate spaces needed for flush right-alignment - # Account for: cmd + ": " + desc + spaces + syntax - # Reduce safety padding from 8 to 2 for tighter right-alignment - VISIBLE_WIDTH=$((CMD_LEN + 2 + DESC_LEN + SYNTAX_LEN)) - NEEDED_SPACES=$((TERM_WIDTH - VISIBLE_WIDTH - 2)) # Minimal padding for flush right + # For tighter right-alignment, calculate remaining space and fill it + # Terminal width - command - ": " - description = space for syntax + LEFT_SIDE_WIDTH=$((CMD_LEN + 2 + DESC_LEN)) + REMAINING_WIDTH=$((TERM_WIDTH - LEFT_SIDE_WIDTH)) # Exact flush-right alignment - # Ensure minimum spacing - [ $NEEDED_SPACES -lt 2 ] && NEEDED_SPACES=2 + # Create padding to push syntax all the way right + # Fill space between description and syntax + FILL_WIDTH=$((REMAINING_WIDTH - SYNTAX_LEN)) + [ $FILL_WIDTH -lt 1 ] && FILL_WIDTH=1 - # Create spacing string - SPACES=$(printf '%*s' "$NEEDED_SPACES" '') + SPACES=$(printf '%*s' "$FILL_WIDTH" '') # Format with colors: # - Bright cyan for command name # - White for description - # - Colorized syntax (right-aligned with cyan cmd, yellow [OPTIONS], magenta FILE, etc.) + # - Colorized syntax (absolutely right-aligned) formatted_line="\033[0;36m${CMD_NAME}\033[0m: ${DESC_PART}${SPACES}${COLORIZED_SYNTAX}" FORMATTED_COMMANDS+=("$formatted_line")