Files
gh-pythoninthegrass-cli-nin…/scripts/combo-search.sh
2025-11-30 08:48:40 +08:00

279 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# combo-search.sh - Example combination workflows using CLI tools
#
# This script demonstrates common patterns for combining fd, rg, ast-grep,
# fzf, jq, and yq to solve real-world repository navigation tasks.
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper function to print section headers
print_section() {
echo -e "\n${BLUE}=== $1 ===${NC}\n"
}
# Helper function to print commands before execution
print_command() {
echo -e "${YELLOW}$ $1${NC}"
}
# 1. Find Python files with TODO comments, select one, and edit
find_and_edit_todos() {
print_section "Find and Edit TODOs"
print_command "rg -l 'TODO' -t py | fzf --preview 'rg -C 3 --color=always TODO {}' | xargs \$EDITOR"
local file
file=$(rg -l 'TODO' -t py | fzf --preview 'rg -C 3 --color=always TODO {}' || true)
if [[ -n "$file" ]]; then
echo -e "${GREEN}Selected: $file${NC}"
# Uncomment to actually open editor:
# $EDITOR "$file"
else
echo -e "${RED}No file selected${NC}"
fi
}
# 2. Find function definitions, select one, and show its usages
find_function_usages() {
print_section "Find Function and Its Usages"
local func_pattern="def $1(\$\$\$):"
print_command "ast-grep --pattern '$func_pattern' -l py | fzf"
local result
result=$(ast-grep --pattern "$func_pattern" -l py | fzf --preview 'bat --color=always {1}' || true)
if [[ -n "$result" ]]; then
local func_name
func_name=$(echo "$result" | sed -n 's/.*def \([^(]*\)(.*/\1/p')
echo -e "${GREEN}Function: $func_name${NC}"
echo -e "${YELLOW}Usages:${NC}"
print_command "rg '\\b$func_name\\(' -t py"
rg "\b$func_name\(" -t py || echo "No usages found"
fi
}
# 3. Find configuration files and edit selected one
edit_config_file() {
print_section "Find and Edit Configuration File"
print_command "fd -e json -e yaml -e toml config | fzf --preview 'bat --color=always {}'"
local config
config=$(fd -e json -e yaml -e toml config | fzf --preview 'bat --color=always {}' || true)
if [[ -n "$config" ]]; then
echo -e "${GREEN}Selected: $config${NC}"
# Uncomment to actually open editor:
# $EDITOR "$config"
else
echo -e "${RED}No config selected${NC}"
fi
}
# 4. Search for imports of a module and show file list
find_module_imports() {
print_section "Find Module Imports"
local module="$1"
print_command "ast-grep --pattern 'from $module import \$\$\$' -l py"
print_command "ast-grep --pattern 'import $module' -l py"
echo -e "${YELLOW}Files importing $module:${NC}"
{
ast-grep --pattern "from $module import \$\$\$" -l py 2>/dev/null || true
ast-grep --pattern "import $module" -l py 2>/dev/null || true
} | sort -u
}
# 5. Find recently modified files and select for editing
edit_recent_file() {
print_section "Find and Edit Recently Modified File"
print_command "fd -t f --changed-within 7d | fzf --preview 'bat --color=always {}'"
local file
file=$(fd -t f --changed-within 7d | fzf --preview 'bat --color=always {}' || true)
if [[ -n "$file" ]]; then
echo -e "${GREEN}Selected: $file${NC}"
# Uncomment to actually open editor:
# $EDITOR "$file"
else
echo -e "${RED}No file selected${NC}"
fi
}
# 6. Find JSON files and explore with jq
explore_json_files() {
print_section "Find and Explore JSON Files"
print_command "fd -e json | fzf --preview 'jq --color-output . {}'"
local json_file
json_file=$(fd -e json | fzf --preview 'jq --color-output . {}' || true)
if [[ -n "$json_file" ]]; then
echo -e "${GREEN}Selected: $json_file${NC}"
echo -e "${YELLOW}Keys:${NC}"
jq 'keys' "$json_file"
else
echo -e "${RED}No JSON file selected${NC}"
fi
}
# 7. Find test files with failures
find_failing_tests() {
print_section "Find Test Files with 'FAIL' or 'ERROR'"
print_command "rg -l '(FAIL|ERROR)' tests/ -t py | fzf --preview 'rg -C 5 --color=always \"(FAIL|ERROR)\" {}'"
local test_file
test_file=$(rg -l '(FAIL|ERROR)' tests/ -t py 2>/dev/null | \
fzf --preview 'rg -C 5 --color=always "(FAIL|ERROR)" {}' || true)
if [[ -n "$test_file" ]]; then
echo -e "${GREEN}Selected: $test_file${NC}"
else
echo -e "${RED}No failing tests found or none selected${NC}"
fi
}
# 8. Find and count function definitions by file
count_functions_per_file() {
print_section "Count Function Definitions Per File"
print_command "ast-grep --pattern 'def \$FUNC(\$\$\$):' -l py | xargs -I {} sh -c 'echo \"{}: \$(ast-grep --pattern \"def \\\$FUNC(\\\$\\\$\\\$):\" {} | wc -l)\"'"
ast-grep --pattern 'def $FUNC($$$):' -l py 2>/dev/null | \
xargs -I {} sh -c 'echo "{}: $(ast-grep --pattern "def \$FUNC(\$\$\$):" {} | wc -l)"' | \
sort -t: -k2 -nr | \
head -10
}
# 9. Find all TODO/FIXME comments and group by file
list_todos_by_file() {
print_section "List TODOs/FIXMEs Grouped by File"
print_command "rg -n '(TODO|FIXME|XXX|HACK):' | sort | fzf --preview 'bat --color=always {1} --highlight-line {2}'"
rg -n '(TODO|FIXME|XXX|HACK):' 2>/dev/null | \
sort | \
fzf --preview 'bat --color=always {1} --highlight-line {2}' || true
}
# 10. Interactive refactoring: find pattern and preview replacement
interactive_refactor() {
print_section "Interactive Refactoring Preview"
local old_pattern="$1"
local new_pattern="$2"
print_command "ast-grep --pattern '$old_pattern' -l py | fzf -m --preview 'ast-grep --pattern \"$old_pattern\" {}'"
local files
files=$(ast-grep --pattern "$old_pattern" -l py 2>/dev/null | \
fzf -m --preview "ast-grep --pattern '$old_pattern' {}" || true)
if [[ -n "$files" ]]; then
echo -e "${GREEN}Selected files for refactoring:${NC}"
echo "$files"
echo -e "\n${YELLOW}To apply refactoring, run:${NC}"
echo -e "ast-grep --pattern '$old_pattern' --rewrite '$new_pattern' -l py --interactive"
else
echo -e "${RED}No files selected${NC}"
fi
}
# Main menu
show_menu() {
echo -e "\n${BLUE}CLI Ninja - Combination Workflows${NC}\n"
echo "1. Find and edit TODOs"
echo "2. Find function usages (requires function name)"
echo "3. Edit config file"
echo "4. Find module imports (requires module name)"
echo "5. Edit recently modified file"
echo "6. Explore JSON files"
echo "7. Find failing tests"
echo "8. Count functions per file"
echo "9. List TODOs/FIXMEs"
echo "10. Interactive refactoring preview (requires old/new patterns)"
echo "0. Exit"
echo
}
# Main execution
main() {
if [[ $# -eq 0 ]]; then
show_menu
read -rp "Select workflow: " choice
case $choice in
1) find_and_edit_todos ;;
2)
read -rp "Enter function name: " func
find_function_usages "$func"
;;
3) edit_config_file ;;
4)
read -rp "Enter module name: " module
find_module_imports "$module"
;;
5) edit_recent_file ;;
6) explore_json_files ;;
7) find_failing_tests ;;
8) count_functions_per_file ;;
9) list_todos_by_file ;;
10)
read -rp "Enter old pattern: " old
read -rp "Enter new pattern: " new
interactive_refactor "$old" "$new"
;;
0) exit 0 ;;
*) echo -e "${RED}Invalid choice${NC}" ;;
esac
else
# Direct command execution
case "$1" in
todos) find_and_edit_todos ;;
function)
[[ $# -ge 2 ]] || { echo "Usage: $0 function <name>"; exit 1; }
find_function_usages "$2"
;;
config) edit_config_file ;;
imports)
[[ $# -ge 2 ]] || { echo "Usage: $0 imports <module>"; exit 1; }
find_module_imports "$2"
;;
recent) edit_recent_file ;;
json) explore_json_files ;;
failing) find_failing_tests ;;
count) count_functions_per_file ;;
list-todos) list_todos_by_file ;;
refactor)
[[ $# -ge 3 ]] || { echo "Usage: $0 refactor <old> <new>"; exit 1; }
interactive_refactor "$2" "$3"
;;
*)
echo "Usage: $0 [command] [args]"
echo "Run without arguments for interactive menu"
exit 1
;;
esac
fi
}
main "$@"