Initial commit
This commit is contained in:
278
scripts/combo-search.sh
Executable file
278
scripts/combo-search.sh
Executable file
@@ -0,0 +1,278 @@
|
||||
#!/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 "$@"
|
||||
436
scripts/interactive-code-finder.sh
Executable file
436
scripts/interactive-code-finder.sh
Executable file
@@ -0,0 +1,436 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# interactive-code-finder.sh - Interactive ast-grep + fzf workflow
|
||||
#
|
||||
# This script provides an interactive way to search for code patterns
|
||||
# using ast-grep and select results with fzf for further action.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
LANGUAGE="${LANGUAGE:-py}"
|
||||
PREVIEW_LINES=20
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE} $1${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓ $1${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗ $1${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${CYAN}ℹ $1${NC}"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠ $1${NC}"
|
||||
}
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies() {
|
||||
local missing=()
|
||||
|
||||
for cmd in ast-grep fzf bat; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
print_error "Missing required tools: ${missing[*]}"
|
||||
echo
|
||||
echo "Install with:"
|
||||
echo " brew install ast-grep fzf bat"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Language-specific pattern examples
|
||||
get_pattern_examples() {
|
||||
local lang="$1"
|
||||
|
||||
case "$lang" in
|
||||
py|python)
|
||||
cat <<EOF
|
||||
Function definitions: def \$FUNC(\$\$\$):
|
||||
Class definitions: class \$CLASS:
|
||||
Function calls: \$FUNC(\$\$\$)
|
||||
Method calls: \$OBJ.\$METHOD(\$\$\$)
|
||||
Imports: from \$MOD import \$\$\$
|
||||
Decorators: @\$DECORATOR
|
||||
If statements: if \$COND:
|
||||
For loops: for \$VAR in \$ITER:
|
||||
Try/except: try: \$\$\$ except \$EXC: \$\$\$
|
||||
Context managers: with \$EXPR as \$VAR:
|
||||
EOF
|
||||
;;
|
||||
js|javascript|ts|typescript)
|
||||
cat <<EOF
|
||||
Function declarations: function \$FUNC(\$\$\$) { \$\$\$ }
|
||||
Arrow functions: (\$\$\$) => \$BODY
|
||||
Class definitions: class \$CLASS { \$\$\$ }
|
||||
Imports: import \$\$ from "\$MODULE"
|
||||
Export default: export default \$EXPR
|
||||
React components: function \$COMP() { \$\$\$ }
|
||||
useState: const [\$STATE, \$SETTER] = useState(\$\$\$)
|
||||
Async functions: async function \$FUNC(\$\$\$) { \$\$\$ }
|
||||
EOF
|
||||
;;
|
||||
rs|rust)
|
||||
cat <<EOF
|
||||
Function definitions: fn \$FUNC(\$\$\$) { \$\$\$ }
|
||||
Struct definitions: struct \$NAME { \$\$\$ }
|
||||
Impl blocks: impl \$TYPE { \$\$\$ }
|
||||
Trait impl: impl \$TRAIT for \$TYPE { \$\$\$ }
|
||||
Macros: \$MACRO!(\$\$\$)
|
||||
Match expressions: match \$EXPR { \$\$\$ }
|
||||
Result returns: fn \$FUNC(\$\$\$) -> Result<\$\$\$> { \$\$\$ }
|
||||
EOF
|
||||
;;
|
||||
go)
|
||||
cat <<EOF
|
||||
Function definitions: func \$FUNC(\$\$\$) \$\$\$ { \$\$\$ }
|
||||
Method definitions: func (\$RECV \$TYPE) \$METHOD(\$\$\$) \$\$\$ { \$\$\$ }
|
||||
Struct definitions: type \$NAME struct { \$\$\$ }
|
||||
Interface definitions: type \$NAME interface { \$\$\$ }
|
||||
Goroutines: go \$FUNC(\$\$\$)
|
||||
Defer statements: defer \$FUNC(\$\$\$)
|
||||
Error checks: if err != nil { \$\$\$ }
|
||||
EOF
|
||||
;;
|
||||
zig)
|
||||
cat <<EOF
|
||||
Public functions: pub fn \$FUNC(\$\$\$) \$\$\$ { \$\$\$ }
|
||||
Struct definitions: const \$NAME = struct { \$\$\$ };
|
||||
Error handling: try \$EXPR
|
||||
Test blocks: test "\$NAME" { \$\$\$ }
|
||||
Comptime: comptime \$EXPR
|
||||
EOF
|
||||
;;
|
||||
rb|ruby)
|
||||
cat <<EOF
|
||||
Method definitions: def \$METHOD(\$\$\$); \$\$\$; end
|
||||
Class definitions: class \$CLASS; \$\$\$; end
|
||||
Blocks with do/end: \$EXPR do |\$PARAM|; \$\$\$; end
|
||||
Blocks with braces: \$EXPR { |\$PARAM| \$\$\$ }
|
||||
Rails routes: get "\$PATH", to: "\$HANDLER"
|
||||
ActiveRecord queries: \$MODEL.where(\$\$\$)
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
echo "Generic pattern: \$PATTERN"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Show pattern examples
|
||||
show_examples() {
|
||||
print_header "Pattern Examples for $LANGUAGE"
|
||||
get_pattern_examples "$LANGUAGE"
|
||||
echo
|
||||
}
|
||||
|
||||
# Interactive pattern input
|
||||
get_pattern() {
|
||||
show_examples
|
||||
|
||||
print_info "Enter your ast-grep pattern (or 'examples' to see more):"
|
||||
echo -e "${YELLOW}Examples:${NC}"
|
||||
echo " - def \$FUNC(\$\$\$):"
|
||||
echo " - class \$CLASS:"
|
||||
echo " - \$FUNC(\$\$\$)"
|
||||
echo
|
||||
|
||||
read -rp "Pattern: " pattern
|
||||
|
||||
if [[ "$pattern" == "examples" ]]; then
|
||||
show_examples
|
||||
get_pattern
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -z "$pattern" ]]; then
|
||||
print_error "Pattern cannot be empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$pattern"
|
||||
}
|
||||
|
||||
# Search with ast-grep and show results with fzf
|
||||
search_pattern() {
|
||||
local pattern="$1"
|
||||
local lang="$2"
|
||||
|
||||
print_info "Searching for: $pattern"
|
||||
echo
|
||||
|
||||
# Create temporary file for results
|
||||
local results
|
||||
results=$(mktemp)
|
||||
|
||||
# Run ast-grep and save results
|
||||
if ! ast-grep --pattern "$pattern" -l "$lang" > "$results" 2>&1; then
|
||||
print_error "ast-grep search failed"
|
||||
cat "$results"
|
||||
rm "$results"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if we have results
|
||||
if [[ ! -s "$results" ]]; then
|
||||
print_warning "No matches found for pattern: $pattern"
|
||||
rm "$results"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Count results
|
||||
local count
|
||||
count=$(wc -l < "$results")
|
||||
print_success "Found $count match(es)"
|
||||
echo
|
||||
|
||||
# Interactive selection with fzf
|
||||
local selected
|
||||
selected=$(cat "$results" | fzf \
|
||||
--preview "bat --color=always --highlight-line {2} {1}" \
|
||||
--preview-window=right:60%:wrap \
|
||||
--delimiter : \
|
||||
--prompt "Select file > " \
|
||||
--header "Press Enter to open, Ctrl-Y to copy path, Ctrl-C to exit" \
|
||||
--bind "ctrl-y:execute-silent(echo {1} | pbcopy)+abort" \
|
||||
--bind "ctrl-/:toggle-preview" \
|
||||
--height=100% \
|
||||
|| true)
|
||||
|
||||
rm "$results"
|
||||
|
||||
if [[ -z "$selected" ]]; then
|
||||
print_info "No file selected"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Extract file path and line number
|
||||
local file line
|
||||
file=$(echo "$selected" | cut -d: -f1)
|
||||
line=$(echo "$selected" | cut -d: -f2)
|
||||
|
||||
# Show action menu
|
||||
show_action_menu "$file" "$line" "$pattern" "$lang"
|
||||
}
|
||||
|
||||
# Action menu for selected file
|
||||
show_action_menu() {
|
||||
local file="$1"
|
||||
local line="$2"
|
||||
local pattern="$3"
|
||||
local lang="$4"
|
||||
|
||||
print_header "Selected: $file:$line"
|
||||
|
||||
echo "What would you like to do?"
|
||||
echo
|
||||
echo "1. Open in editor (\$EDITOR)"
|
||||
echo "2. Show context (20 lines)"
|
||||
echo "3. Show all matches in file"
|
||||
echo "4. Copy file path to clipboard"
|
||||
echo "5. Find usages of pattern in project"
|
||||
echo "6. Exit"
|
||||
echo
|
||||
|
||||
read -rp "Choice: " choice
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
print_info "Opening $file at line $line..."
|
||||
${EDITOR:-vim} "+$line" "$file"
|
||||
;;
|
||||
2)
|
||||
print_header "Context around $file:$line"
|
||||
bat --color=always --highlight-line "$line" --line-range "$((line-10)):$((line+10))" "$file"
|
||||
;;
|
||||
3)
|
||||
print_header "All matches in $file"
|
||||
ast-grep --pattern "$pattern" -l "$lang" "$file"
|
||||
;;
|
||||
4)
|
||||
echo -n "$file" | pbcopy
|
||||
print_success "Copied to clipboard: $file"
|
||||
;;
|
||||
5)
|
||||
print_header "Finding usages in project..."
|
||||
# Extract the identifier from the pattern
|
||||
local identifier
|
||||
identifier=$(echo "$pattern" | grep -oE '\$[A-Z_]+' | head -1 | sed 's/\$//')
|
||||
if [[ -n "$identifier" ]]; then
|
||||
print_info "Searching for references to: $identifier"
|
||||
rg "\b$identifier\b" -t "$lang"
|
||||
else
|
||||
print_warning "Could not extract identifier from pattern"
|
||||
fi
|
||||
;;
|
||||
6)
|
||||
print_info "Exiting..."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Invalid choice"
|
||||
show_action_menu "$file" "$line" "$pattern" "$lang"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Multi-pattern search mode
|
||||
multi_pattern_search() {
|
||||
print_header "Multi-Pattern Search"
|
||||
|
||||
print_info "Enter patterns (one per line, empty line to finish):"
|
||||
local patterns=()
|
||||
|
||||
while true; do
|
||||
read -rp "Pattern $(( ${#patterns[@]} + 1 )): " pattern
|
||||
if [[ -z "$pattern" ]]; then
|
||||
break
|
||||
fi
|
||||
patterns+=("$pattern")
|
||||
done
|
||||
|
||||
if [[ ${#patterns[@]} -eq 0 ]]; then
|
||||
print_error "No patterns provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "Searching for ${#patterns[@]} pattern(s)..."
|
||||
|
||||
local all_results
|
||||
all_results=$(mktemp)
|
||||
|
||||
for pattern in "${patterns[@]}"; do
|
||||
ast-grep --pattern "$pattern" -l "$LANGUAGE" >> "$all_results" 2>&1 || true
|
||||
done
|
||||
|
||||
# Remove duplicates and sort
|
||||
sort -u "$all_results" | fzf \
|
||||
--preview "bat --color=always {1}" \
|
||||
--preview-window=right:60%:wrap \
|
||||
--delimiter : \
|
||||
--multi \
|
||||
--prompt "Select files > " \
|
||||
--header "Tab to select multiple, Enter to confirm" \
|
||||
--bind "ctrl-a:select-all" \
|
||||
--bind "ctrl-d:deselect-all" \
|
||||
--height=100% \
|
||||
|| true
|
||||
|
||||
rm "$all_results"
|
||||
}
|
||||
|
||||
# Help message
|
||||
show_help() {
|
||||
cat <<EOF
|
||||
${BLUE}Interactive Code Finder${NC}
|
||||
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Options:
|
||||
-p, --pattern PATTERN Search for specific pattern
|
||||
-l, --language LANG Set language (default: py)
|
||||
-m, --multi Multi-pattern search mode
|
||||
-e, --examples Show pattern examples and exit
|
||||
-h, --help Show this help message
|
||||
|
||||
Languages:
|
||||
py, python Python
|
||||
js, javascript JavaScript
|
||||
ts, typescript TypeScript
|
||||
rs, rust Rust
|
||||
go Go
|
||||
zig Zig
|
||||
rb, ruby Ruby
|
||||
|
||||
Examples:
|
||||
$0 # Interactive mode
|
||||
$0 -p "def \$FUNC(\$\$\$):" # Search for Python functions
|
||||
$0 -l js -p "function \$F(\$\$\$)" # Search for JS functions
|
||||
$0 -m # Multi-pattern search
|
||||
$0 -e # Show pattern examples
|
||||
|
||||
Pattern Syntax:
|
||||
\$VAR - Single node (identifier, expression)
|
||||
\$\$\$ - Zero or more nodes (variadic)
|
||||
\$\$ - Statement sequence
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local pattern=""
|
||||
local multi_mode=false
|
||||
|
||||
# Check dependencies first
|
||||
check_dependencies
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-p|--pattern)
|
||||
pattern="$2"
|
||||
shift 2
|
||||
;;
|
||||
-l|--language)
|
||||
LANGUAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-m|--multi)
|
||||
multi_mode=true
|
||||
shift
|
||||
;;
|
||||
-e|--examples)
|
||||
show_examples
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Multi-pattern mode
|
||||
if [[ "$multi_mode" == true ]]; then
|
||||
multi_pattern_search
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get pattern if not provided
|
||||
if [[ -z "$pattern" ]]; then
|
||||
pattern=$(get_pattern)
|
||||
fi
|
||||
|
||||
# Search
|
||||
search_pattern "$pattern" "$LANGUAGE"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user