Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "cli-ninja",
|
||||||
|
"description": "Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"author": {
|
||||||
|
"name": "pythoninthegrass",
|
||||||
|
"email": "pythoninthegrass@users.noreply.github.com"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# cli-ninja
|
||||||
|
|
||||||
|
Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)
|
||||||
540
SKILL.md
Normal file
540
SKILL.md
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
---
|
||||||
|
name: cli-ninja
|
||||||
|
description: Master CLI navigation and code exploration using modern command-line tools. Use this skill when navigating repositories, searching for files/text/code patterns, or working with structured data (JSON/YAML/XML). Provides guidance on fd (file finding), rg (text search), ast-grep (code structure), fzf (interactive selection), jq (JSON), and yq (YAML/XML).
|
||||||
|
---
|
||||||
|
|
||||||
|
# CLI Ninja
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill provides guidance for efficient repository navigation and code exploration using modern CLI tools. Each tool serves a specific purpose: `fd` for finding files, `rg` for searching text, `ast-grep` for understanding code structure, `fzf` for interactive selection, `jq` for JSON manipulation, and `yq` for YAML/XML processing.
|
||||||
|
|
||||||
|
## Tool Selection Decision Tree
|
||||||
|
|
||||||
|
When navigating or exploring a repository, choose the appropriate tool based on the task:
|
||||||
|
|
||||||
|
```
|
||||||
|
What are you looking for?
|
||||||
|
|
||||||
|
├─ FILES by name/path/extension?
|
||||||
|
│ └─ Use: fd
|
||||||
|
│ └─ Need to select from results? → Pipe to fzf
|
||||||
|
│
|
||||||
|
├─ TEXT/STRINGS in file contents?
|
||||||
|
│ └─ Use: rg (ripgrep)
|
||||||
|
│ └─ Need to select from results? → Pipe to fzf
|
||||||
|
│
|
||||||
|
├─ CODE STRUCTURE (functions, classes, imports)?
|
||||||
|
│ └─ Use: ast-grep
|
||||||
|
│ └─ Need to select from results? → Pipe to fzf
|
||||||
|
│ └─ Need to refactor/replace? → Use ast-grep --rewrite
|
||||||
|
│
|
||||||
|
├─ Working with JSON data?
|
||||||
|
│ └─ Use: jq
|
||||||
|
│
|
||||||
|
└─ Working with YAML or XML data?
|
||||||
|
└─ Use: yq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference Guide
|
||||||
|
|
||||||
|
### fd - Find Files
|
||||||
|
|
||||||
|
**Basic patterns:**
|
||||||
|
```bash
|
||||||
|
# Find by name
|
||||||
|
fd 'pattern'
|
||||||
|
|
||||||
|
# Find by extension
|
||||||
|
fd -e py # All Python files
|
||||||
|
fd -e 'test.py$' # Files ending in test.py
|
||||||
|
|
||||||
|
# Find in specific directory
|
||||||
|
fd 'pattern' src/
|
||||||
|
|
||||||
|
# Case-insensitive
|
||||||
|
fd -i readme
|
||||||
|
|
||||||
|
# Hidden files
|
||||||
|
fd -H '.env'
|
||||||
|
|
||||||
|
# Exclude patterns
|
||||||
|
fd -e py -E '*test*'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common workflows:**
|
||||||
|
```bash
|
||||||
|
# Find and edit a config file interactively
|
||||||
|
fd config | fzf | xargs $EDITOR
|
||||||
|
|
||||||
|
# Find all Python files modified in last 7 days
|
||||||
|
fd -e py -td --changed-within 7d
|
||||||
|
|
||||||
|
# Find files by size
|
||||||
|
fd -e db --size +100m
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/fd-patterns.md` for comprehensive patterns and flags.
|
||||||
|
|
||||||
|
### rg - Search Text/Strings
|
||||||
|
|
||||||
|
**Basic patterns:**
|
||||||
|
```bash
|
||||||
|
# Basic search
|
||||||
|
rg 'search_term'
|
||||||
|
|
||||||
|
# Case-insensitive
|
||||||
|
rg -i 'search_term'
|
||||||
|
|
||||||
|
# Whole word only
|
||||||
|
rg -w 'word'
|
||||||
|
|
||||||
|
# With context lines
|
||||||
|
rg -C 3 'pattern' # 3 lines before/after
|
||||||
|
rg -B 2 -A 5 'pattern' # 2 before, 5 after
|
||||||
|
|
||||||
|
# Specific file types
|
||||||
|
rg -t py 'import' # Python files
|
||||||
|
rg -t rust 'fn main' # Rust files
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advanced usage:**
|
||||||
|
```bash
|
||||||
|
# Search with line numbers and file names
|
||||||
|
rg -n 'pattern'
|
||||||
|
|
||||||
|
# Multiline search
|
||||||
|
rg -U 'pattern.*spanning.*lines'
|
||||||
|
|
||||||
|
# Exclude patterns
|
||||||
|
rg 'TODO' -g '!*test*'
|
||||||
|
|
||||||
|
# Search in specific directories
|
||||||
|
rg 'pattern' src/ core/
|
||||||
|
|
||||||
|
# JSON output for scripting
|
||||||
|
rg --json 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interactive workflows:**
|
||||||
|
```bash
|
||||||
|
# Search and open in editor
|
||||||
|
rg -l 'TODO' | fzf | xargs $EDITOR
|
||||||
|
|
||||||
|
# Search with preview
|
||||||
|
rg --line-number 'pattern' | fzf --preview 'bat --color=always {1} --highlight-line {2}'
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/rg-patterns.md` for regex tips and advanced patterns.
|
||||||
|
|
||||||
|
### ast-grep - Find Code Structure
|
||||||
|
|
||||||
|
**Philosophy:** ast-grep searches code by structure, not strings. It understands programming language syntax and semantics.
|
||||||
|
|
||||||
|
**Basic patterns:**
|
||||||
|
|
||||||
|
**Python:**
|
||||||
|
```bash
|
||||||
|
# Find all function definitions
|
||||||
|
ast-grep --pattern 'def $FUNC($$$)' -l py
|
||||||
|
|
||||||
|
# Find specific function calls
|
||||||
|
ast-grep --pattern 'player.play($$$)' -l py
|
||||||
|
|
||||||
|
# Find class definitions
|
||||||
|
ast-grep --pattern 'class $CLASS:' -l py
|
||||||
|
|
||||||
|
# Find all imports of a module
|
||||||
|
ast-grep --pattern 'from $MOD import $$$' -l py
|
||||||
|
ast-grep --pattern 'import $MOD' -l py
|
||||||
|
|
||||||
|
# Find type annotations
|
||||||
|
ast-grep --pattern 'def $FUNC($$$) -> $TYPE:' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
**JavaScript/TypeScript:**
|
||||||
|
```bash
|
||||||
|
# Find React components
|
||||||
|
ast-grep --pattern 'function $COMP() { $$$ }' -l tsx
|
||||||
|
|
||||||
|
# Find useState hooks
|
||||||
|
ast-grep --pattern 'const [$STATE, $SETTER] = useState($$$)' -l tsx
|
||||||
|
|
||||||
|
# Find async functions
|
||||||
|
ast-grep --pattern 'async function $FUNC($$$) { $$$ }' -l js
|
||||||
|
|
||||||
|
# Find imports
|
||||||
|
ast-grep --pattern 'import $$ from "$MODULE"' -l ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rust:**
|
||||||
|
```bash
|
||||||
|
# Find function definitions
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Find struct definitions
|
||||||
|
ast-grep --pattern 'struct $NAME { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Find impl blocks
|
||||||
|
ast-grep --pattern 'impl $TRAIT for $TYPE { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Find macro usage
|
||||||
|
ast-grep --pattern 'println!($$$)' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Go:**
|
||||||
|
```bash
|
||||||
|
# Find function definitions
|
||||||
|
ast-grep --pattern 'func $FUNC($$$) $$$ { $$$ }' -l go
|
||||||
|
|
||||||
|
# Find struct definitions
|
||||||
|
ast-grep --pattern 'type $NAME struct { $$$ }' -l go
|
||||||
|
|
||||||
|
# Find interface implementations
|
||||||
|
ast-grep --pattern 'func ($RECV $TYPE) $METHOD($$$) $$$ { $$$ }' -l go
|
||||||
|
|
||||||
|
# Find goroutines
|
||||||
|
ast-grep --pattern 'go $FUNC($$$)' -l go
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zig:**
|
||||||
|
```bash
|
||||||
|
# Find function definitions
|
||||||
|
ast-grep --pattern 'pub fn $FUNC($$$) $$$ { $$$ }' -l zig
|
||||||
|
|
||||||
|
# Find struct definitions
|
||||||
|
ast-grep --pattern 'const $NAME = struct { $$$ };' -l zig
|
||||||
|
|
||||||
|
# Find test functions
|
||||||
|
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
|
||||||
|
|
||||||
|
# Find error handling
|
||||||
|
ast-grep --pattern 'try $EXPR' -l zig
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ruby:**
|
||||||
|
```bash
|
||||||
|
# Find method definitions
|
||||||
|
ast-grep --pattern 'def $METHOD($$$); $$$; end' -l rb
|
||||||
|
|
||||||
|
# Find class definitions
|
||||||
|
ast-grep --pattern 'class $CLASS; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Find blocks
|
||||||
|
ast-grep --pattern '$EXPR do |$PARAM|; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Find Rails routes
|
||||||
|
ast-grep --pattern 'get "$PATH", to: "$HANDLER"' -l rb
|
||||||
|
```
|
||||||
|
|
||||||
|
**Refactoring with ast-grep:**
|
||||||
|
```bash
|
||||||
|
# Rename a function across the codebase
|
||||||
|
ast-grep --pattern 'old_function($$$)' --rewrite 'new_function($$$)' -l py --interactive
|
||||||
|
|
||||||
|
# Update API calls
|
||||||
|
ast-grep --pattern 'api.v1.$METHOD($$$)' --rewrite 'api.v2.$METHOD($$$)' -l py
|
||||||
|
|
||||||
|
# Modernize code patterns
|
||||||
|
ast-grep --pattern 'var $VAR = $EXPR' --rewrite 'const $VAR = $EXPR' -l js
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interactive workflows:**
|
||||||
|
```bash
|
||||||
|
# Find and select a function to edit
|
||||||
|
ast-grep --pattern 'def $FUNC($$$)' -l py | fzf | cut -d: -f1 | xargs $EDITOR
|
||||||
|
|
||||||
|
# Find all TODOs in code comments (use rg instead)
|
||||||
|
rg 'TODO|FIXME' | fzf
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/ast-grep-guide.md` for comprehensive patterns across all languages.
|
||||||
|
|
||||||
|
### fzf - Interactive Selection
|
||||||
|
|
||||||
|
**Basic usage:**
|
||||||
|
```bash
|
||||||
|
# Pipe any list to fzf for selection
|
||||||
|
command | fzf
|
||||||
|
|
||||||
|
# Multi-select mode
|
||||||
|
command | fzf -m
|
||||||
|
|
||||||
|
# With preview window
|
||||||
|
command | fzf --preview 'bat {}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Preview configurations:**
|
||||||
|
```bash
|
||||||
|
# Preview files with syntax highlighting
|
||||||
|
fd -t f | fzf --preview 'bat --color=always {}'
|
||||||
|
|
||||||
|
# Preview with line numbers
|
||||||
|
rg --line-number 'pattern' | fzf --preview 'bat --color=always {1} --highlight-line {2}'
|
||||||
|
|
||||||
|
# Preview JSON files
|
||||||
|
fd -e json | fzf --preview 'jq . {}'
|
||||||
|
|
||||||
|
# Custom preview window size
|
||||||
|
fzf --preview 'cat {}' --preview-window=right:50%
|
||||||
|
```
|
||||||
|
|
||||||
|
**Keybindings:**
|
||||||
|
```bash
|
||||||
|
# ctrl-t: Paste selected files
|
||||||
|
# ctrl-r: Paste from history
|
||||||
|
# alt-c: cd into selected directory
|
||||||
|
|
||||||
|
# Custom keybindings
|
||||||
|
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)'
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/fzf-workflows.md` for advanced interactive workflows.
|
||||||
|
|
||||||
|
### jq - JSON Processing
|
||||||
|
|
||||||
|
**Basic queries:**
|
||||||
|
```bash
|
||||||
|
# Pretty print
|
||||||
|
jq . file.json
|
||||||
|
|
||||||
|
# Extract field
|
||||||
|
jq '.name' file.json
|
||||||
|
|
||||||
|
# Array indexing
|
||||||
|
jq '.[0]' file.json
|
||||||
|
|
||||||
|
# Nested access
|
||||||
|
jq '.user.name' file.json
|
||||||
|
|
||||||
|
# Array operations
|
||||||
|
jq '.[] | .name' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common transformations:**
|
||||||
|
```bash
|
||||||
|
# Filter arrays
|
||||||
|
jq '.users[] | select(.active == true)' file.json
|
||||||
|
|
||||||
|
# Map over arrays
|
||||||
|
jq '.users | map(.name)' file.json
|
||||||
|
|
||||||
|
# Sorting
|
||||||
|
jq '.users | sort_by(.age)' file.json
|
||||||
|
|
||||||
|
# Group by
|
||||||
|
jq 'group_by(.category)' file.json
|
||||||
|
|
||||||
|
# Count items
|
||||||
|
jq '.items | length' file.json
|
||||||
|
|
||||||
|
# Keys extraction
|
||||||
|
jq 'keys' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advanced usage:**
|
||||||
|
```bash
|
||||||
|
# Multiple filters
|
||||||
|
jq '.users[] | select(.age > 18) | .name' file.json
|
||||||
|
|
||||||
|
# Construct new objects
|
||||||
|
jq '{name: .firstName, email: .emailAddress}' file.json
|
||||||
|
|
||||||
|
# Combine multiple files
|
||||||
|
jq -s '.[0] + .[1]' file1.json file2.json
|
||||||
|
|
||||||
|
# Read from stdin
|
||||||
|
curl -s https://api.example.com/data | jq '.results[]'
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/jq-cookbook.md` for comprehensive transformations.
|
||||||
|
|
||||||
|
### yq - YAML/XML Processing
|
||||||
|
|
||||||
|
**YAML queries:**
|
||||||
|
```bash
|
||||||
|
# Pretty print
|
||||||
|
yq . file.yaml
|
||||||
|
|
||||||
|
# Extract field
|
||||||
|
yq '.name' file.yaml
|
||||||
|
|
||||||
|
# Nested access
|
||||||
|
yq '.services.web.image' docker-compose.yml
|
||||||
|
|
||||||
|
# Array operations
|
||||||
|
yq '.dependencies[]' file.yaml
|
||||||
|
|
||||||
|
# Convert to JSON
|
||||||
|
yq -o json . file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
**XML queries:**
|
||||||
|
```bash
|
||||||
|
# Parse XML
|
||||||
|
yq -p xml . file.xml
|
||||||
|
|
||||||
|
# Extract elements
|
||||||
|
yq -p xml '.root.element' file.xml
|
||||||
|
|
||||||
|
# Convert XML to JSON
|
||||||
|
yq -p xml -o json . file.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common operations:**
|
||||||
|
```bash
|
||||||
|
# Update values
|
||||||
|
yq '.version = "2.0"' file.yaml
|
||||||
|
|
||||||
|
# Merge files
|
||||||
|
yq '. *= load("other.yaml")' file.yaml
|
||||||
|
|
||||||
|
# Filter arrays
|
||||||
|
yq '.items[] | select(.enabled == true)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
See `references/yq-examples.md` for YAML/XML processing patterns.
|
||||||
|
|
||||||
|
## Combination Workflows
|
||||||
|
|
||||||
|
Real-world tasks often combine multiple tools:
|
||||||
|
|
||||||
|
### 1. Find and Edit Files with Context
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find Python files containing a function, preview them, then edit
|
||||||
|
rg -l 'def process_data' -t py | fzf --preview 'rg -C 5 "def process_data" {}' | xargs $EDITOR
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Code Refactoring Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all usages of a function across the codebase
|
||||||
|
ast-grep --pattern 'old_function($$$)' -l py | fzf -m
|
||||||
|
|
||||||
|
# Then refactor with interactive confirmation
|
||||||
|
ast-grep --pattern 'old_function($$$)' --rewrite 'new_function($$$)' -l py --interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Interactive Configuration Explorer
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find config files and explore their structure
|
||||||
|
fd -e json -e yaml | fzf --preview 'bat --color=always {} --style=numbers'
|
||||||
|
|
||||||
|
# Or with jq/yq preview
|
||||||
|
fd -e json | fzf --preview 'jq . {}'
|
||||||
|
fd -e yaml | fzf --preview 'yq . {}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Find Related Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find a class definition, then find all its usages
|
||||||
|
CLASS=$(ast-grep --pattern 'class $NAME:' -l py | fzf | cut -d: -f2)
|
||||||
|
rg -w "$CLASS" --type py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Dependency Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all imports of a module
|
||||||
|
ast-grep --pattern 'from mymodule import $$$' -l py -t python
|
||||||
|
|
||||||
|
# Combine with text search for dynamic imports
|
||||||
|
rg 'importlib.*mymodule' -t py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Multi-File Search and Replace
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find files with pattern, select multiple, and apply changes
|
||||||
|
rg -l 'TODO.*urgent' | fzf -m | xargs sed -i '' 's/TODO.*urgent/DONE/g'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Code Structure Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all functions in a module, count them
|
||||||
|
ast-grep --pattern 'def $FUNC($$$)' -l py src/module.py | wc -l
|
||||||
|
|
||||||
|
# Find function complexity (functions with many parameters)
|
||||||
|
ast-grep --pattern 'def $FUNC($A, $B, $C, $D, $E, $$$)' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
See `scripts/combo-search.sh` and `scripts/interactive-code-finder.sh` for reusable combination workflows.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Performance Tips
|
||||||
|
|
||||||
|
1. **Use the right tool for the job:**
|
||||||
|
- File names → `fd`
|
||||||
|
- Text content → `rg`
|
||||||
|
- Code structure → `ast-grep`
|
||||||
|
|
||||||
|
2. **Narrow your search scope:**
|
||||||
|
```bash
|
||||||
|
# Good: Search specific directory
|
||||||
|
rg 'pattern' src/
|
||||||
|
|
||||||
|
# Better: Search specific file types
|
||||||
|
rg 'pattern' -t py src/
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Use `.gitignore` respect:**
|
||||||
|
- Both `fd` and `rg` respect `.gitignore` by default
|
||||||
|
- Use `-u` or `--no-ignore` to override when needed
|
||||||
|
|
||||||
|
### Pattern Matching Tips
|
||||||
|
|
||||||
|
1. **ast-grep patterns:**
|
||||||
|
- Use `$VAR` for single identifiers
|
||||||
|
- Use `$$$` for arbitrary code sequences
|
||||||
|
- Use `$$` for statement lists
|
||||||
|
|
||||||
|
2. **ripgrep regex:**
|
||||||
|
- Use `-F` for literal strings (faster)
|
||||||
|
- Use `-w` for whole words
|
||||||
|
- Use `\b` for word boundaries
|
||||||
|
|
||||||
|
3. **fzf fuzzy matching:**
|
||||||
|
- Space for AND search: `foo bar` matches files with both
|
||||||
|
- `|` for OR search: `foo|bar`
|
||||||
|
- `^` for prefix match, `$` for suffix match
|
||||||
|
|
||||||
|
### Debugging Searches
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See what rg is searching
|
||||||
|
rg --debug 'pattern' 2>&1 | head -20
|
||||||
|
|
||||||
|
# Dry-run ast-grep refactoring
|
||||||
|
ast-grep --pattern 'old' --rewrite 'new' --interactive
|
||||||
|
|
||||||
|
# Test jq queries incrementally
|
||||||
|
echo '{"name":"test"}' | jq .
|
||||||
|
echo '{"name":"test"}' | jq '.name'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
This skill includes bundled resources to support CLI navigation:
|
||||||
|
|
||||||
|
### scripts/
|
||||||
|
|
||||||
|
Example scripts demonstrating tool combinations:
|
||||||
|
|
||||||
|
- `combo-search.sh` - Common combination workflows
|
||||||
|
- `interactive-code-finder.sh` - Interactive ast-grep + fzf workflow
|
||||||
|
|
||||||
|
### references/
|
||||||
|
|
||||||
|
Comprehensive guides for each tool:
|
||||||
|
|
||||||
|
- `fd-patterns.md` - File finding patterns and flags
|
||||||
|
- `rg-patterns.md` - Text search patterns and regex tips
|
||||||
|
- `ast-grep-guide.md` - Code structure patterns for Python, Zig, Go, Ruby, JS/TS, Rust
|
||||||
|
- `fzf-workflows.md` - Interactive selection and preview configurations
|
||||||
|
- `jq-cookbook.md` - JSON transformations and filters
|
||||||
|
- `yq-examples.md` - YAML/XML processing examples
|
||||||
77
plugin.lock.json
Normal file
77
plugin.lock.json
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:pythoninthegrass/cli-ninja:cli-ninja",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "a20e7b3373b41171100bffe83f9d51875dc770e1",
|
||||||
|
"treeHash": "f975f1775bfc70bc6ba04673373843287d4c39ec40e06920c8a02aec140c224e",
|
||||||
|
"generatedAt": "2025-11-28T10:27:42.424911Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "cli-ninja",
|
||||||
|
"description": "Master CLI navigation and code exploration using modern command-line tools (fd, rg, ast-grep, fzf, jq, yq)",
|
||||||
|
"version": "0.1.1"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "2b48fd08069259ae5df45b55ac4089b43bcb5c85dbcd248351588c17d18a1623"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "SKILL.md",
|
||||||
|
"sha256": "8092b5ec021baf61c1380305c0cba776eb6951bca2dd70a5fbe587938305fe13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/fzf-workflows.md",
|
||||||
|
"sha256": "ccb1f98dfd990fcb61e7d22a7b84fc8e39cb71ee18412ba4765d27da95481907"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/yq-examples.md",
|
||||||
|
"sha256": "c9d285023eb62fc76c7b57a8b80245b41ee2ae12481dcf29d191473c724a4f06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/ast-grep-guide.md",
|
||||||
|
"sha256": "a30432790d889a3b3ed72fc6145510f8fcedd7b598830ffca0486d3a7139d25d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/jq-cookbook.md",
|
||||||
|
"sha256": "f1035915e749cb214b8ea603d7851e67e5881f1f28b6df6df865a5f778b49ce6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/fd-patterns.md",
|
||||||
|
"sha256": "14f1c11aa3a07d4c318ebd9766313ee6683b4ab0f649883b67662f6c6416439f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "references/rg-patterns.md",
|
||||||
|
"sha256": "85aac5efca24cc69cfca9b9a7c594d1f31b625c2f043607fd7d9555ee4a35b09"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "scripts/interactive-code-finder.sh",
|
||||||
|
"sha256": "2b545e560b6e29fbae8b070bed84e0ed355151331ae23015fbcc4edd484797ff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "scripts/combo-search.sh",
|
||||||
|
"sha256": "f7e79545b424c43f33e43611388a26e40e62fd0395f35a58217c40c7c5133e05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "09558028c80425968afb13e08743fe45a629f86e73bdf71a54effc6f65edefeb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "f975f1775bfc70bc6ba04673373843287d4c39ec40e06920c8a02aec140c224e"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
680
references/ast-grep-guide.md
Normal file
680
references/ast-grep-guide.md
Normal file
@@ -0,0 +1,680 @@
|
|||||||
|
# ast-grep - Code Structure Search Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`ast-grep` searches code by Abstract Syntax Tree (AST) structure, not text. It understands code semantics and can find patterns that are impossible with regex.
|
||||||
|
|
||||||
|
## Core Concepts
|
||||||
|
|
||||||
|
### Pattern Syntax
|
||||||
|
|
||||||
|
- `$VAR` - Matches a single AST node (identifier, expression, etc.)
|
||||||
|
- `$$$` - Matches zero or more nodes (variadic)
|
||||||
|
- `$$` - Matches a sequence of statements
|
||||||
|
|
||||||
|
### Pattern Matching Philosophy
|
||||||
|
|
||||||
|
ast-grep matches **structure**, not **text**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# This finds ALL function calls to 'print', regardless of arguments
|
||||||
|
ast-grep --pattern 'print($$$)'
|
||||||
|
|
||||||
|
# Matches:
|
||||||
|
# print()
|
||||||
|
# print("hello")
|
||||||
|
# print("hello", "world")
|
||||||
|
# print(x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Language Support
|
||||||
|
|
||||||
|
ast-grep supports: Python, JavaScript, TypeScript, Rust, Go, Java, C, C++, C#, Ruby, Kotlin, Swift, and more.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Python Patterns
|
||||||
|
|
||||||
|
### Function Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Any function
|
||||||
|
ast-grep --pattern 'def $FUNC($$$): $$$' -l py
|
||||||
|
|
||||||
|
# Functions with no parameters
|
||||||
|
ast-grep --pattern 'def $FUNC(): $$$' -l py
|
||||||
|
|
||||||
|
# Functions with specific number of parameters
|
||||||
|
ast-grep --pattern 'def $FUNC($A, $B): $$$' -l py
|
||||||
|
|
||||||
|
# Functions with type hints
|
||||||
|
ast-grep --pattern 'def $FUNC($$$) -> $TYPE: $$$' -l py
|
||||||
|
|
||||||
|
# Async functions
|
||||||
|
ast-grep --pattern 'async def $FUNC($$$): $$$' -l py
|
||||||
|
|
||||||
|
# Methods (functions with self)
|
||||||
|
ast-grep --pattern 'def $METHOD(self, $$$): $$$' -l py
|
||||||
|
|
||||||
|
# Class methods
|
||||||
|
ast-grep --pattern '@classmethod' -A 1 -l py
|
||||||
|
|
||||||
|
# Static methods
|
||||||
|
ast-grep --pattern '@staticmethod' -A 1 -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Any class
|
||||||
|
ast-grep --pattern 'class $CLASS: $$$' -l py
|
||||||
|
|
||||||
|
# Class with inheritance
|
||||||
|
ast-grep --pattern 'class $CLASS($BASE): $$$' -l py
|
||||||
|
|
||||||
|
# Class with multiple inheritance
|
||||||
|
ast-grep --pattern 'class $CLASS($$$): $$$' -l py
|
||||||
|
|
||||||
|
# Dataclass
|
||||||
|
ast-grep --pattern '@dataclass' -A 1 -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Imports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Import statements
|
||||||
|
ast-grep --pattern 'import $MODULE' -l py
|
||||||
|
|
||||||
|
# From imports
|
||||||
|
ast-grep --pattern 'from $MODULE import $$$' -l py
|
||||||
|
|
||||||
|
# Specific import
|
||||||
|
ast-grep --pattern 'from $MODULE import $NAME' -l py
|
||||||
|
|
||||||
|
# Relative imports
|
||||||
|
ast-grep --pattern 'from . import $$$' -l py
|
||||||
|
ast-grep --pattern 'from .. import $$$' -l py
|
||||||
|
|
||||||
|
# Import aliases
|
||||||
|
ast-grep --pattern 'import $MODULE as $ALIAS' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Function Calls
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Any call to a function
|
||||||
|
ast-grep --pattern '$FUNC($$$)' -l py
|
||||||
|
|
||||||
|
# Method calls
|
||||||
|
ast-grep --pattern '$OBJ.$METHOD($$$)' -l py
|
||||||
|
|
||||||
|
# Chained method calls
|
||||||
|
ast-grep --pattern '$OBJ.$M1().$M2($$$)' -l py
|
||||||
|
|
||||||
|
# Specific function calls
|
||||||
|
ast-grep --pattern 'print($$$)' -l py
|
||||||
|
ast-grep --pattern 'open($$$)' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Control Flow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If statements
|
||||||
|
ast-grep --pattern 'if $COND: $$$' -l py
|
||||||
|
|
||||||
|
# For loops
|
||||||
|
ast-grep --pattern 'for $VAR in $ITER: $$$' -l py
|
||||||
|
|
||||||
|
# While loops
|
||||||
|
ast-grep --pattern 'while $COND: $$$' -l py
|
||||||
|
|
||||||
|
# Try/except
|
||||||
|
ast-grep --pattern 'try: $$$ except $EXC: $$$' -l py
|
||||||
|
|
||||||
|
# Context managers
|
||||||
|
ast-grep --pattern 'with $EXPR as $VAR: $$$' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Decorators
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Any decorator
|
||||||
|
ast-grep --pattern '@$DECORATOR' -l py
|
||||||
|
|
||||||
|
# Specific decorator
|
||||||
|
ast-grep --pattern '@property' -l py
|
||||||
|
ast-grep --pattern '@staticmethod' -l py
|
||||||
|
|
||||||
|
# Decorator with arguments
|
||||||
|
ast-grep --pattern '@$DECORATOR($$$)' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## JavaScript/TypeScript Patterns
|
||||||
|
|
||||||
|
### Function Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Function declarations
|
||||||
|
ast-grep --pattern 'function $FUNC($$$) { $$$ }' -l js
|
||||||
|
|
||||||
|
# Arrow functions
|
||||||
|
ast-grep --pattern '($$$) => $BODY' -l js
|
||||||
|
ast-grep --pattern '$VAR = ($$$) => { $$$ }' -l js
|
||||||
|
|
||||||
|
# Async functions
|
||||||
|
ast-grep --pattern 'async function $FUNC($$$) { $$$ }' -l js
|
||||||
|
ast-grep --pattern 'async ($$$) => $BODY' -l js
|
||||||
|
|
||||||
|
# Generator functions
|
||||||
|
ast-grep --pattern 'function* $FUNC($$$) { $$$ }' -l js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class and Components
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Class declarations
|
||||||
|
ast-grep --pattern 'class $CLASS { $$$ }' -l js
|
||||||
|
|
||||||
|
# Class with extends
|
||||||
|
ast-grep --pattern 'class $CLASS extends $BASE { $$$ }' -l js
|
||||||
|
|
||||||
|
# React functional components
|
||||||
|
ast-grep --pattern 'function $COMP() { $$$ }' -l tsx
|
||||||
|
ast-grep --pattern 'const $COMP = () => { $$$ }' -l tsx
|
||||||
|
|
||||||
|
# React class components
|
||||||
|
ast-grep --pattern 'class $COMP extends React.Component { $$$ }' -l tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Imports/Exports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Import statements
|
||||||
|
ast-grep --pattern 'import $$ from "$MODULE"' -l js
|
||||||
|
|
||||||
|
# Named imports
|
||||||
|
ast-grep --pattern 'import { $$ } from "$MODULE"' -l js
|
||||||
|
|
||||||
|
# Default import
|
||||||
|
ast-grep --pattern 'import $DEFAULT from "$MODULE"' -l js
|
||||||
|
|
||||||
|
# Require (CommonJS)
|
||||||
|
ast-grep --pattern 'require("$MODULE")' -l js
|
||||||
|
|
||||||
|
# Export default
|
||||||
|
ast-grep --pattern 'export default $EXPR' -l js
|
||||||
|
|
||||||
|
# Named exports
|
||||||
|
ast-grep --pattern 'export { $$ }' -l js
|
||||||
|
```
|
||||||
|
|
||||||
|
### React Hooks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# useState
|
||||||
|
ast-grep --pattern 'const [$STATE, $SETTER] = useState($$$)' -l tsx
|
||||||
|
|
||||||
|
# useEffect
|
||||||
|
ast-grep --pattern 'useEffect($$$)' -l tsx
|
||||||
|
|
||||||
|
# useCallback
|
||||||
|
ast-grep --pattern 'useCallback($$$)' -l tsx
|
||||||
|
|
||||||
|
# useMemo
|
||||||
|
ast-grep --pattern 'useMemo($$$)' -l tsx
|
||||||
|
|
||||||
|
# Custom hooks (by convention)
|
||||||
|
ast-grep --pattern 'function use$HOOK($$$) { $$$ }' -l tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript Specifics
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Type definitions
|
||||||
|
ast-grep --pattern 'type $NAME = $TYPE' -l ts
|
||||||
|
|
||||||
|
# Interface definitions
|
||||||
|
ast-grep --pattern 'interface $NAME { $$$ }' -l ts
|
||||||
|
|
||||||
|
# Type assertions
|
||||||
|
ast-grep --pattern '$EXPR as $TYPE' -l ts
|
||||||
|
|
||||||
|
# Generic functions
|
||||||
|
ast-grep --pattern 'function $FUNC<$$$>($$$) { $$$ }' -l ts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rust Patterns
|
||||||
|
|
||||||
|
### Function Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Function declarations
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Functions with return type
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) -> $TYPE { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Public functions
|
||||||
|
ast-grep --pattern 'pub fn $FUNC($$$) { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Async functions
|
||||||
|
ast-grep --pattern 'async fn $FUNC($$$) { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Unsafe functions
|
||||||
|
ast-grep --pattern 'unsafe fn $FUNC($$$) { $$$ }' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structs and Enums
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Struct definitions
|
||||||
|
ast-grep --pattern 'struct $NAME { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Tuple structs
|
||||||
|
ast-grep --pattern 'struct $NAME($$$);' -l rs
|
||||||
|
|
||||||
|
# Enum definitions
|
||||||
|
ast-grep --pattern 'enum $NAME { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Public structs
|
||||||
|
ast-grep --pattern 'pub struct $NAME { $$$ }' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Impl Blocks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Implementation blocks
|
||||||
|
ast-grep --pattern 'impl $TYPE { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Trait implementations
|
||||||
|
ast-grep --pattern 'impl $TRAIT for $TYPE { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Generic implementations
|
||||||
|
ast-grep --pattern 'impl<$$$> $TYPE { $$$ }' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Macros
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Macro usage
|
||||||
|
ast-grep --pattern '$MACRO!($$$)' -l rs
|
||||||
|
|
||||||
|
# Specific macros
|
||||||
|
ast-grep --pattern 'println!($$$)' -l rs
|
||||||
|
ast-grep --pattern 'vec![$$$]' -l rs
|
||||||
|
|
||||||
|
# Macro definitions
|
||||||
|
ast-grep --pattern 'macro_rules! $NAME { $$$ }' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Result returns
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) -> Result<$$$> { $$$ }' -l rs
|
||||||
|
|
||||||
|
# Unwrap calls
|
||||||
|
ast-grep --pattern '$EXPR.unwrap()' -l rs
|
||||||
|
|
||||||
|
# Question mark operator
|
||||||
|
ast-grep --pattern '$EXPR?' -l rs
|
||||||
|
|
||||||
|
# Match Result
|
||||||
|
ast-grep --pattern 'match $EXPR { Ok($VAL) => $$$, Err($ERR) => $$$ }' -l rs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Go Patterns
|
||||||
|
|
||||||
|
### Function Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Function declarations
|
||||||
|
ast-grep --pattern 'func $FUNC($$$) $$$ { $$$ }' -l go
|
||||||
|
|
||||||
|
# Methods
|
||||||
|
ast-grep --pattern 'func ($RECV $TYPE) $METHOD($$$) $$$ { $$$ }' -l go
|
||||||
|
|
||||||
|
# Functions with multiple returns
|
||||||
|
ast-grep --pattern 'func $FUNC($$$) ($$$, error) { $$$ }' -l go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Struct and Interface
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Struct definitions
|
||||||
|
ast-grep --pattern 'type $NAME struct { $$$ }' -l go
|
||||||
|
|
||||||
|
# Interface definitions
|
||||||
|
ast-grep --pattern 'type $NAME interface { $$$ }' -l go
|
||||||
|
|
||||||
|
# Type aliases
|
||||||
|
ast-grep --pattern 'type $NAME $TYPE' -l go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Concurrency
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Goroutine launches
|
||||||
|
ast-grep --pattern 'go $FUNC($$$)' -l go
|
||||||
|
|
||||||
|
# Channel operations
|
||||||
|
ast-grep --pattern '$CHAN <- $VALUE' -l go
|
||||||
|
ast-grep --pattern '$VAR := <-$CHAN' -l go
|
||||||
|
|
||||||
|
# Select statements
|
||||||
|
ast-grep --pattern 'select { $$$ }' -l go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If err check
|
||||||
|
ast-grep --pattern 'if err != nil { $$$ }' -l go
|
||||||
|
|
||||||
|
# Error returns
|
||||||
|
ast-grep --pattern 'return $$$, err' -l go
|
||||||
|
|
||||||
|
# Defer statements
|
||||||
|
ast-grep --pattern 'defer $FUNC($$$)' -l go
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Zig Patterns
|
||||||
|
|
||||||
|
### Function Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Public functions
|
||||||
|
ast-grep --pattern 'pub fn $FUNC($$$) $$$ { $$$ }' -l zig
|
||||||
|
|
||||||
|
# Private functions
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) $$$ { $$$ }' -l zig
|
||||||
|
|
||||||
|
# Exported functions
|
||||||
|
ast-grep --pattern 'export fn $FUNC($$$) $$$ { $$$ }' -l zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structs and Types
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Struct definitions
|
||||||
|
ast-grep --pattern 'const $NAME = struct { $$$ };' -l zig
|
||||||
|
|
||||||
|
# Packed structs
|
||||||
|
ast-grep --pattern 'const $NAME = packed struct { $$$ };' -l zig
|
||||||
|
|
||||||
|
# Type definitions
|
||||||
|
ast-grep --pattern 'const $NAME = $TYPE;' -l zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Try expressions
|
||||||
|
ast-grep --pattern 'try $EXPR' -l zig
|
||||||
|
|
||||||
|
# Catch
|
||||||
|
ast-grep --pattern '$EXPR catch $$$' -l zig
|
||||||
|
|
||||||
|
# Error unions
|
||||||
|
ast-grep --pattern 'fn $FUNC($$$) !$$$ { $$$ }' -l zig
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test blocks
|
||||||
|
ast-grep --pattern 'test "$NAME" { $$$ }' -l zig
|
||||||
|
|
||||||
|
# Expect calls
|
||||||
|
ast-grep --pattern 'try testing.expect($$$)' -l zig
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ruby Patterns
|
||||||
|
|
||||||
|
### Method Definitions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Method definitions
|
||||||
|
ast-grep --pattern 'def $METHOD($$$); $$$; end' -l rb
|
||||||
|
|
||||||
|
# Class methods
|
||||||
|
ast-grep --pattern 'def self.$METHOD($$$); $$$; end' -l rb
|
||||||
|
|
||||||
|
# Private methods
|
||||||
|
ast-grep --pattern 'private' -A 1 -l rb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Classes and Modules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Class definitions
|
||||||
|
ast-grep --pattern 'class $CLASS; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Class with inheritance
|
||||||
|
ast-grep --pattern 'class $CLASS < $BASE; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Module definitions
|
||||||
|
ast-grep --pattern 'module $MODULE; $$$; end' -l rb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Blocks and Iterators
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Blocks with do/end
|
||||||
|
ast-grep --pattern '$EXPR do |$PARAM|; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Blocks with braces
|
||||||
|
ast-grep --pattern '$EXPR { |$PARAM| $$$ }' -l rb
|
||||||
|
|
||||||
|
# Each iterator
|
||||||
|
ast-grep --pattern '$COLLECTION.each do |$ITEM|; $$$; end' -l rb
|
||||||
|
|
||||||
|
# Map
|
||||||
|
ast-grep --pattern '$COLLECTION.map { |$ITEM| $$$ }' -l rb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rails Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Route definitions
|
||||||
|
ast-grep --pattern 'get "$PATH", to: "$HANDLER"' -l rb
|
||||||
|
ast-grep --pattern 'post "$PATH", to: "$HANDLER"' -l rb
|
||||||
|
|
||||||
|
# ActiveRecord queries
|
||||||
|
ast-grep --pattern '$MODEL.where($$$)' -l rb
|
||||||
|
ast-grep --pattern '$MODEL.find_by($$$)' -l rb
|
||||||
|
|
||||||
|
# Validations
|
||||||
|
ast-grep --pattern 'validates $$$' -l rb
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Refactoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rename function
|
||||||
|
ast-grep --pattern 'old_function($$$)' \
|
||||||
|
--rewrite 'new_function($$$)' \
|
||||||
|
-l py --interactive
|
||||||
|
|
||||||
|
# Update API version
|
||||||
|
ast-grep --pattern 'api.v1.$METHOD($$$)' \
|
||||||
|
--rewrite 'api.v2.$METHOD($$$)' \
|
||||||
|
-l py
|
||||||
|
|
||||||
|
# Modernize var to const
|
||||||
|
ast-grep --pattern 'var $VAR = $EXPR' \
|
||||||
|
--rewrite 'const $VAR = $EXPR' \
|
||||||
|
-l js --interactive
|
||||||
|
|
||||||
|
# Add type annotations
|
||||||
|
ast-grep --pattern 'def $FUNC($PARAM):' \
|
||||||
|
--rewrite 'def $FUNC($PARAM: Any):' \
|
||||||
|
-l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Pattern Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search with multiple patterns (OR)
|
||||||
|
ast-grep --pattern 'print($$$)' -l py && \
|
||||||
|
ast-grep --pattern 'logging.$METHOD($$$)' -l py
|
||||||
|
|
||||||
|
# Chain searches (AND)
|
||||||
|
ast-grep --pattern 'def $FUNC($$$):' -l py | \
|
||||||
|
grep -v test | \
|
||||||
|
xargs -I {} ast-grep --pattern 'return $$$' {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complex Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find functions with many parameters (code smell)
|
||||||
|
ast-grep --pattern 'def $FUNC($A, $B, $C, $D, $E, $$$):' -l py
|
||||||
|
|
||||||
|
# Find deeply nested function calls
|
||||||
|
ast-grep --pattern '$A($B($C($$$)))' -l py
|
||||||
|
|
||||||
|
# Find unused variables (assigned but not used)
|
||||||
|
ast-grep --pattern '$VAR = $$$' -l py # Then analyze
|
||||||
|
|
||||||
|
# Find mutable default arguments (Python anti-pattern)
|
||||||
|
ast-grep --pattern 'def $FUNC($PARAM=[]):' -l py
|
||||||
|
ast-grep --pattern 'def $FUNC($PARAM={}):' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find console.log statements
|
||||||
|
ast-grep --pattern 'console.log($$$)' -l js
|
||||||
|
|
||||||
|
# Find debugger statements
|
||||||
|
ast-grep --pattern 'debugger' -l js
|
||||||
|
|
||||||
|
# Find print debugging
|
||||||
|
ast-grep --pattern 'print($$$)' -l py
|
||||||
|
|
||||||
|
# Find TODO comments in code (not comments, but in strings)
|
||||||
|
ast-grep --pattern '"TODO: $$$"' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rule Files
|
||||||
|
|
||||||
|
Create reusable rules in YAML:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# rules.yml
|
||||||
|
id: no-mutable-default
|
||||||
|
language: python
|
||||||
|
rule:
|
||||||
|
pattern: 'def $FUNC($$$, $PARAM=[], $$$):'
|
||||||
|
message: Avoid mutable default arguments
|
||||||
|
severity: warning
|
||||||
|
```
|
||||||
|
|
||||||
|
Use with:
|
||||||
|
```bash
|
||||||
|
ast-grep scan --rule rules.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Create `.ast-grep.yml` in project root:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ruleDirs:
|
||||||
|
- rules
|
||||||
|
testDirs:
|
||||||
|
- tests
|
||||||
|
ignore:
|
||||||
|
- node_modules
|
||||||
|
- .venv
|
||||||
|
- dist
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
1. **Use language specification**: `-l py` is more accurate
|
||||||
|
2. **Narrow scope**: Search specific directories
|
||||||
|
3. **Use specific patterns**: More specific = faster
|
||||||
|
4. **Cache results**: Output to file for repeated analysis
|
||||||
|
|
||||||
|
## Interactive Workflows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and select function to edit
|
||||||
|
ast-grep --pattern 'def $FUNC($$$):' -l py | \
|
||||||
|
fzf --preview 'bat --color=always {1}' | \
|
||||||
|
cut -d: -f1 | \
|
||||||
|
xargs $EDITOR
|
||||||
|
|
||||||
|
# Find usages and refactor
|
||||||
|
ast-grep --pattern 'old_name($$$)' -l py | \
|
||||||
|
fzf -m | \
|
||||||
|
xargs ast-grep --pattern 'old_name($$$)' --rewrite 'new_name($$$)' --interactive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
### Finding Security Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SQL injection risks
|
||||||
|
ast-grep --pattern 'execute("$$$" + $VAR)' -l py
|
||||||
|
|
||||||
|
# XSS risks
|
||||||
|
ast-grep --pattern 'innerHTML = $$$' -l js
|
||||||
|
|
||||||
|
# Command injection
|
||||||
|
ast-grep --pattern 'os.system($$$)' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Quality Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find long functions (structural complexity)
|
||||||
|
ast-grep --pattern 'def $FUNC($$$): $$$' -l py | \
|
||||||
|
awk -F: '{print $1":"$2}' | \
|
||||||
|
xargs -I {} sh -c 'echo {}; sed -n "{}p" | wc -l'
|
||||||
|
|
||||||
|
# Find classes with many methods
|
||||||
|
ast-grep --pattern 'class $CLASS: $$$' -l py
|
||||||
|
|
||||||
|
# Find deeply nested code
|
||||||
|
ast-grep --pattern 'if $A: if $B: if $C: $$$' -l py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find undocumented functions
|
||||||
|
ast-grep --pattern 'def $FUNC($$$):' -l py | \
|
||||||
|
xargs rg -L '"""' # Functions without docstrings
|
||||||
|
|
||||||
|
# Find public APIs
|
||||||
|
ast-grep --pattern 'export function $FUNC($$$) { $$$ }' -l ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comparison with grep/rg
|
||||||
|
|
||||||
|
| Task | grep/rg | ast-grep |
|
||||||
|
|------|---------|----------|
|
||||||
|
| Find string "print" | `rg print` | N/A |
|
||||||
|
| Find print function calls | `rg 'print\('` (fragile) | `ast-grep --pattern 'print($$$)'` |
|
||||||
|
| Find function definitions | `rg '^def '` (limited) | `ast-grep --pattern 'def $F($$$):'` |
|
||||||
|
| Rename function | Complex sed | `--rewrite` flag |
|
||||||
|
| Find by structure | Not possible | Native |
|
||||||
290
references/fd-patterns.md
Normal file
290
references/fd-patterns.md
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
# fd - File Finding Patterns
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`fd` is a fast, user-friendly alternative to `find`. It respects `.gitignore` by default and uses intuitive patterns.
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find files/directories by name (case-insensitive by default)
|
||||||
|
fd pattern
|
||||||
|
|
||||||
|
# Case-sensitive search
|
||||||
|
fd -s Pattern
|
||||||
|
|
||||||
|
# Case-insensitive explicitly
|
||||||
|
fd -i pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Type Filtering
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# By extension
|
||||||
|
fd -e py # All .py files
|
||||||
|
fd -e js -e ts # Multiple extensions
|
||||||
|
|
||||||
|
# By type
|
||||||
|
fd -t f # Files only
|
||||||
|
fd -t d # Directories only
|
||||||
|
fd -t l # Symlinks only
|
||||||
|
fd -t x # Executable files only
|
||||||
|
|
||||||
|
# Combine type and extension
|
||||||
|
fd -t f -e py # Python files only
|
||||||
|
```
|
||||||
|
|
||||||
|
## Search Scope
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search in specific directory
|
||||||
|
fd pattern /path/to/dir
|
||||||
|
|
||||||
|
# Multiple directories
|
||||||
|
fd pattern dir1/ dir2/
|
||||||
|
|
||||||
|
# Depth control
|
||||||
|
fd -d 1 pattern # Current directory only
|
||||||
|
fd -d 3 pattern # Up to 3 levels deep
|
||||||
|
fd --max-depth 2 # Alternative syntax
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Filtering
|
||||||
|
|
||||||
|
### By Size
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Files larger than 100MB
|
||||||
|
fd -t f --size +100m
|
||||||
|
|
||||||
|
# Files smaller than 1KB
|
||||||
|
fd -t f --size -1k
|
||||||
|
|
||||||
|
# Files between 10KB and 1MB
|
||||||
|
fd -t f --size +10k --size -1m
|
||||||
|
```
|
||||||
|
|
||||||
|
### By Time
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Modified in last 7 days
|
||||||
|
fd --changed-within 7d
|
||||||
|
|
||||||
|
# Modified more than 30 days ago
|
||||||
|
fd --changed-before 30d
|
||||||
|
|
||||||
|
# Specific date ranges
|
||||||
|
fd --changed-within 2024-01-01..2024-12-31
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hidden and Ignored Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Include hidden files
|
||||||
|
fd -H pattern
|
||||||
|
|
||||||
|
# Ignore .gitignore rules
|
||||||
|
fd -u pattern
|
||||||
|
|
||||||
|
# Both hidden and ignored
|
||||||
|
fd -Hu pattern
|
||||||
|
|
||||||
|
# Search in .git directories
|
||||||
|
fd -H -u -I pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
## Exclusion Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Exclude specific patterns
|
||||||
|
fd -E '*.pyc' -E '__pycache__' pattern
|
||||||
|
|
||||||
|
# Exclude directories
|
||||||
|
fd -E 'node_modules' -E '.git' pattern
|
||||||
|
|
||||||
|
# Multiple exclusions
|
||||||
|
fd -e py -E '*test*' -E '*_pb2.py'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full paths
|
||||||
|
fd -a pattern
|
||||||
|
|
||||||
|
# Relative paths (default)
|
||||||
|
fd pattern
|
||||||
|
|
||||||
|
# Print absolute paths
|
||||||
|
fd -p pattern
|
||||||
|
|
||||||
|
# Null-separated output (for xargs -0)
|
||||||
|
fd -0 pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern Matching
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Exact name match
|
||||||
|
fd -g 'config.json'
|
||||||
|
|
||||||
|
# Wildcard patterns
|
||||||
|
fd -g '*.test.js'
|
||||||
|
fd -g 'test_*.py'
|
||||||
|
|
||||||
|
# Regex mode (default)
|
||||||
|
fd '^[A-Z].*\.py$'
|
||||||
|
|
||||||
|
# Fixed string (faster)
|
||||||
|
fd -F 'literal.string'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Execute command on each result
|
||||||
|
fd -e py -x python -m py_compile {}
|
||||||
|
|
||||||
|
# Execute with multiple files
|
||||||
|
fd -e txt -X cat
|
||||||
|
|
||||||
|
# Execute with placeholders
|
||||||
|
fd -e md -x echo "File: {}" "Path: {/}" "Dir: {//}"
|
||||||
|
|
||||||
|
# Parallel execution
|
||||||
|
fd -e py -x -j 4 pylint {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### Find and Edit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and edit config files
|
||||||
|
fd config | fzf | xargs $EDITOR
|
||||||
|
|
||||||
|
# Find recent Python files and edit
|
||||||
|
fd -e py --changed-within 1d | fzf | xargs $EDITOR
|
||||||
|
```
|
||||||
|
|
||||||
|
### Find and Delete
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and remove cache files
|
||||||
|
fd -e pyc -X rm
|
||||||
|
fd -t d __pycache__ -X rm -rf
|
||||||
|
|
||||||
|
# Interactive deletion
|
||||||
|
fd '*.log' -x rm -i {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Find and Copy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy all Python files to backup
|
||||||
|
fd -e py -X cp -t backup/
|
||||||
|
|
||||||
|
# Copy with directory structure
|
||||||
|
fd -e py -x cp --parents {} backup/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Statistics and Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Count files by type
|
||||||
|
fd -e py | wc -l
|
||||||
|
|
||||||
|
# List largest files
|
||||||
|
fd -t f --size +1m -x ls -lh {} | sort -k5 -hr
|
||||||
|
|
||||||
|
# Find duplicate filenames
|
||||||
|
fd | awk -F/ '{print $NF}' | sort | uniq -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
1. **Narrow scope early**: Use `-d` to limit depth, `-t` to filter type
|
||||||
|
2. **Use specific patterns**: More specific patterns are faster
|
||||||
|
3. **Respect .gitignore**: Default behavior is already optimized
|
||||||
|
4. **Use fixed strings**: `-F` is faster than regex when possible
|
||||||
|
|
||||||
|
## Common Use Cases
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find test files
|
||||||
|
fd test -e py
|
||||||
|
fd -g '*_test.go'
|
||||||
|
fd -g '*.test.ts'
|
||||||
|
|
||||||
|
# Find configuration files
|
||||||
|
fd -e json -e yaml -e toml config
|
||||||
|
|
||||||
|
# Find source files excluding tests
|
||||||
|
fd -e py -E '*test*'
|
||||||
|
|
||||||
|
# Find files modified today
|
||||||
|
fd --changed-within 1d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all markdown files
|
||||||
|
fd -e md
|
||||||
|
|
||||||
|
# Find README files
|
||||||
|
fd -i readme
|
||||||
|
|
||||||
|
# Find documentation directories
|
||||||
|
fd -t d docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Artifacts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find compiled files
|
||||||
|
fd -e o -e so -e dylib
|
||||||
|
|
||||||
|
# Find build directories
|
||||||
|
fd -t d -g 'build' -g 'dist' -g 'target'
|
||||||
|
|
||||||
|
# Find and clean
|
||||||
|
fd -e pyc -E '.venv' -X rm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comparison with find
|
||||||
|
|
||||||
|
| Task | find | fd |
|
||||||
|
|------|------|-----|
|
||||||
|
| Find by name | `find . -name '*.py'` | `fd -e py` |
|
||||||
|
| Files only | `find . -type f` | `fd -t f` |
|
||||||
|
| Max depth | `find . -maxdepth 2` | `fd -d 2` |
|
||||||
|
| Exclude pattern | `find . ! -path '*/test/*'` | `fd -E '*test*'` |
|
||||||
|
| Execute command | `find . -name '*.py' -exec python {} \;` | `fd -e py -x python {}` |
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
1. **Combine with other tools**:
|
||||||
|
```bash
|
||||||
|
fd -e py | xargs wc -l # Count lines
|
||||||
|
fd -e md | xargs grip -b # Preview markdown
|
||||||
|
fd -0 | xargs -0 tar czf # Create archive
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use shell aliases**:
|
||||||
|
```bash
|
||||||
|
alias fdd='fd -t d' # Directories only
|
||||||
|
alias fdf='fd -t f' # Files only
|
||||||
|
alias fdh='fd -H' # Include hidden
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create functions**:
|
||||||
|
```bash
|
||||||
|
# Find and cd into directory
|
||||||
|
fcd() { cd $(fd -t d "$1" | fzf) }
|
||||||
|
|
||||||
|
# Find and open in editor
|
||||||
|
fe() { $EDITOR $(fd "$1" | fzf) }
|
||||||
|
```
|
||||||
589
references/fzf-workflows.md
Normal file
589
references/fzf-workflows.md
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
# fzf - Interactive Selection Workflows
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`fzf` is a command-line fuzzy finder that enables interactive selection from lists. It's a powerful tool for building interactive workflows.
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pipe any list to fzf
|
||||||
|
ls | fzf
|
||||||
|
|
||||||
|
# Multi-select with Tab
|
||||||
|
ls | fzf -m
|
||||||
|
|
||||||
|
# Use selected result
|
||||||
|
SELECTED=$(ls | fzf)
|
||||||
|
echo "You selected: $SELECTED"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preview Windows
|
||||||
|
|
||||||
|
### Basic Preview
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preview with cat
|
||||||
|
fd | fzf --preview 'cat {}'
|
||||||
|
|
||||||
|
# Preview with bat (syntax highlighting)
|
||||||
|
fd | fzf --preview 'bat --color=always {}'
|
||||||
|
|
||||||
|
# Preview with line numbers
|
||||||
|
fd | fzf --preview 'bat --color=always --line-range :500 {}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Preview Window Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Position and size
|
||||||
|
fzf --preview 'cat {}' --preview-window=right:50%
|
||||||
|
fzf --preview 'cat {}' --preview-window=up:40%
|
||||||
|
fzf --preview 'cat {}' --preview-window=down:30%
|
||||||
|
fzf --preview 'cat {}' --preview-window=left:50%
|
||||||
|
|
||||||
|
# Hidden by default (toggle with ctrl-/)
|
||||||
|
fzf --preview 'cat {}' --preview-window=hidden
|
||||||
|
|
||||||
|
# Wrap text
|
||||||
|
fzf --preview 'cat {}' --preview-window=wrap
|
||||||
|
|
||||||
|
# No wrap (default)
|
||||||
|
fzf --preview 'cat {}' --preview-window=nowrap
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Previews
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preview with highlighted search term
|
||||||
|
rg --line-number 'pattern' | \
|
||||||
|
fzf --delimiter : \
|
||||||
|
--preview 'bat --color=always {1} --highlight-line {2}'
|
||||||
|
|
||||||
|
# Preview JSON with jq
|
||||||
|
fd -e json | fzf --preview 'jq --color-output . {}'
|
||||||
|
|
||||||
|
# Preview images (requires chafa/imgcat)
|
||||||
|
fd -e png -e jpg | fzf --preview 'chafa {}'
|
||||||
|
|
||||||
|
# Preview directories
|
||||||
|
fd -t d | fzf --preview 'ls -la {}'
|
||||||
|
|
||||||
|
# Preview with tree
|
||||||
|
fd -t d | fzf --preview 'tree -L 2 {}'
|
||||||
|
|
||||||
|
# Multi-level preview (file type detection)
|
||||||
|
fd | fzf --preview '[[ -f {} ]] && bat --color=always {} || tree -L 1 {}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keybindings
|
||||||
|
|
||||||
|
### Default Keybindings
|
||||||
|
|
||||||
|
```
|
||||||
|
ctrl-j / ctrl-n / down : Move down
|
||||||
|
ctrl-k / ctrl-p / up : Move up
|
||||||
|
enter : Select and exit
|
||||||
|
tab : Mark item (multi-select)
|
||||||
|
shift-tab : Unmark item
|
||||||
|
ctrl-c / esc : Exit without selection
|
||||||
|
ctrl-/ : Toggle preview window
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Keybindings
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy to clipboard (macOS)
|
||||||
|
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)'
|
||||||
|
|
||||||
|
# Copy to clipboard (Linux)
|
||||||
|
fzf --bind 'ctrl-y:execute-silent(echo {} | xclip -selection clipboard)'
|
||||||
|
|
||||||
|
# Open in editor
|
||||||
|
fzf --bind 'ctrl-e:execute($EDITOR {})'
|
||||||
|
|
||||||
|
# Open in editor and exit
|
||||||
|
fzf --bind 'ctrl-e:execute($EDITOR {})+abort'
|
||||||
|
|
||||||
|
# Delete file with confirmation
|
||||||
|
fzf --bind 'ctrl-d:execute(rm -i {})'
|
||||||
|
|
||||||
|
# Multiple bindings
|
||||||
|
fzf --bind 'ctrl-e:execute($EDITOR {})' \
|
||||||
|
--bind 'ctrl-y:execute-silent(echo {} | pbcopy)' \
|
||||||
|
--bind 'ctrl-d:execute(rm -i {})'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Keybinding Actions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Reload results
|
||||||
|
fzf --bind 'ctrl-r:reload(fd)'
|
||||||
|
|
||||||
|
# Change preview
|
||||||
|
fzf --preview 'bat {}' \
|
||||||
|
--bind 'ctrl-/:change-preview-window(hidden|)'
|
||||||
|
|
||||||
|
# Toggle between preview styles
|
||||||
|
fzf --preview 'bat --color=always {}' \
|
||||||
|
--bind 'ctrl-t:change-preview(tree -L 2 {})'
|
||||||
|
|
||||||
|
# Accept and execute
|
||||||
|
fzf --bind 'enter:execute(echo Selected: {})+accept'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Search Syntax
|
||||||
|
|
||||||
|
### Fuzzy Matching
|
||||||
|
|
||||||
|
```
|
||||||
|
# Basic fuzzy search
|
||||||
|
abc # Matches files containing 'a', 'b', 'c' in order
|
||||||
|
# Examples: abc, aXXbXXc, axbxc
|
||||||
|
|
||||||
|
# Exact match (single quote)
|
||||||
|
'abc # Matches files containing exact string "abc"
|
||||||
|
|
||||||
|
# Prefix match (caret)
|
||||||
|
^abc # Matches files starting with "abc"
|
||||||
|
|
||||||
|
# Suffix match (dollar)
|
||||||
|
abc$ # Matches files ending with "abc"
|
||||||
|
|
||||||
|
# Exact match (both)
|
||||||
|
^abc$ # Matches exactly "abc"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Logical Operators
|
||||||
|
|
||||||
|
```
|
||||||
|
# AND (space)
|
||||||
|
abc def # Contains both "abc" AND "def"
|
||||||
|
|
||||||
|
# OR (pipe)
|
||||||
|
abc|def # Contains "abc" OR "def"
|
||||||
|
|
||||||
|
# NOT (exclamation)
|
||||||
|
abc !def # Contains "abc" but NOT "def"
|
||||||
|
|
||||||
|
# Combination
|
||||||
|
abc def|ghi !jkl
|
||||||
|
# Contains "abc" AND ("def" OR "ghi") but NOT "jkl"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Field Selection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search specific field (delimiter-separated)
|
||||||
|
# -d : Set delimiter
|
||||||
|
# -n : Select field number (1-indexed)
|
||||||
|
# --with-nth : Display specific fields
|
||||||
|
|
||||||
|
# Example: search only filenames in paths
|
||||||
|
fd | fzf -d / --with-nth -1
|
||||||
|
|
||||||
|
# Example: search only line numbers
|
||||||
|
rg --line-number 'pattern' | fzf -d : --with-nth 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### File Navigation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and edit file
|
||||||
|
fe() {
|
||||||
|
local file
|
||||||
|
file=$(fd --type f | fzf --preview 'bat --color=always {}')
|
||||||
|
[[ -n "$file" ]] && $EDITOR "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find and cd to directory
|
||||||
|
fcd() {
|
||||||
|
local dir
|
||||||
|
dir=$(fd --type d | fzf --preview 'tree -L 1 {}')
|
||||||
|
[[ -n "$dir" ]] && cd "$dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find file with content preview
|
||||||
|
ff() {
|
||||||
|
local file
|
||||||
|
file=$(rg --files-with-matches "${1:-.}" | \
|
||||||
|
fzf --preview "rg --color=always --context 5 '$1' {}")
|
||||||
|
[[ -n "$file" ]] && $EDITOR "$file"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search content and edit
|
||||||
|
fzf_grep() {
|
||||||
|
local line
|
||||||
|
line=$(rg --line-number "$1" | \
|
||||||
|
fzf --delimiter : \
|
||||||
|
--preview 'bat --color=always {1} --highlight-line {2}' \
|
||||||
|
--preview-window +{2}-/2)
|
||||||
|
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
local file=$(echo "$line" | cut -d: -f1)
|
||||||
|
local lineno=$(echo "$line" | cut -d: -f2)
|
||||||
|
$EDITOR "+$lineno" "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find function definition and edit
|
||||||
|
find_function() {
|
||||||
|
local result
|
||||||
|
result=$(ast-grep --pattern "def $1(\$\$\$):" | \
|
||||||
|
fzf --preview 'bat --color=always {1}')
|
||||||
|
|
||||||
|
if [[ -n "$result" ]]; then
|
||||||
|
local file=$(echo "$result" | cut -d: -f1)
|
||||||
|
$EDITOR "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Git Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive git add
|
||||||
|
fga() {
|
||||||
|
git status --short | \
|
||||||
|
fzf -m --preview 'git diff --color=always {2}' | \
|
||||||
|
awk '{print $2}' | \
|
||||||
|
xargs git add
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive git checkout
|
||||||
|
fgco() {
|
||||||
|
git branch --all | \
|
||||||
|
grep -v HEAD | \
|
||||||
|
sed 's/^[* ]*//' | \
|
||||||
|
sed 's#remotes/origin/##' | \
|
||||||
|
sort -u | \
|
||||||
|
fzf --preview 'git log --oneline --color=always {}' | \
|
||||||
|
xargs git checkout
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive git log
|
||||||
|
fgl() {
|
||||||
|
git log --oneline --color=always | \
|
||||||
|
fzf --ansi --preview 'git show --color=always {1}' | \
|
||||||
|
awk '{print $1}' | \
|
||||||
|
xargs git show
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive git diff
|
||||||
|
fgd() {
|
||||||
|
git diff --name-only | \
|
||||||
|
fzf --preview 'git diff --color=always {}' | \
|
||||||
|
xargs git diff
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Process Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive kill
|
||||||
|
fkill() {
|
||||||
|
local pid
|
||||||
|
pid=$(ps aux | \
|
||||||
|
sed 1d | \
|
||||||
|
fzf -m --header='Select process to kill' | \
|
||||||
|
awk '{print $2}')
|
||||||
|
|
||||||
|
if [[ -n "$pid" ]]; then
|
||||||
|
echo "$pid" | xargs kill -${1:-9}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive lsof (find what's using a port)
|
||||||
|
fport() {
|
||||||
|
local port
|
||||||
|
port=$(lsof -i -P -n | \
|
||||||
|
sed 1d | \
|
||||||
|
fzf --header='Select connection' | \
|
||||||
|
awk '{print $2}')
|
||||||
|
|
||||||
|
[[ -n "$port" ]] && echo "PID: $port"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive container selection
|
||||||
|
fdocker() {
|
||||||
|
docker ps -a | \
|
||||||
|
sed 1d | \
|
||||||
|
fzf --header='Select container' | \
|
||||||
|
awk '{print $1}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive container logs
|
||||||
|
fdlogs() {
|
||||||
|
local container
|
||||||
|
container=$(fdocker)
|
||||||
|
[[ -n "$container" ]] && docker logs -f "$container"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive container exec
|
||||||
|
fdexec() {
|
||||||
|
local container
|
||||||
|
container=$(fdocker)
|
||||||
|
[[ -n "$container" ]] && docker exec -it "$container" /bin/bash
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Package Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Interactive npm script runner
|
||||||
|
fnpm() {
|
||||||
|
local script
|
||||||
|
script=$(cat package.json | \
|
||||||
|
jq -r '.scripts | keys[]' | \
|
||||||
|
fzf --preview 'jq -r ".scripts.{}" package.json')
|
||||||
|
|
||||||
|
[[ -n "$script" ]] && npm run "$script"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interactive pip package info
|
||||||
|
fpip() {
|
||||||
|
pip list | \
|
||||||
|
fzf --preview 'pip show {1}'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit config file
|
||||||
|
fconf() {
|
||||||
|
local file
|
||||||
|
file=$(fd -H -t f \
|
||||||
|
-e conf -e config -e json -e yaml -e yml -e toml -e ini \
|
||||||
|
. "$HOME/.config" | \
|
||||||
|
fzf --preview 'bat --color=always {}')
|
||||||
|
|
||||||
|
[[ -n "$file" ]] && $EDITOR "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Edit dotfile
|
||||||
|
fdot() {
|
||||||
|
local file
|
||||||
|
file=$(fd -H -t f '^\..*' "$HOME" --max-depth 1 | \
|
||||||
|
fzf --preview 'bat --color=always {}')
|
||||||
|
|
||||||
|
[[ -n "$file" ]] && $EDITOR "$file"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Configurations
|
||||||
|
|
||||||
|
### Color Schemes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Monokai
|
||||||
|
export FZF_DEFAULT_OPTS='
|
||||||
|
--color=fg:#f8f8f2,bg:#272822,hl:#66d9ef
|
||||||
|
--color=fg+:#f8f8f2,bg+:#44475a,hl+:#66d9ef
|
||||||
|
--color=info:#a6e22e,prompt:#f92672,pointer:#f92672
|
||||||
|
--color=marker:#f92672,spinner:#a6e22e,header:#6272a4
|
||||||
|
'
|
||||||
|
|
||||||
|
# Nord
|
||||||
|
export FZF_DEFAULT_OPTS='
|
||||||
|
--color=fg:#e5e9f0,bg:#3b4252,hl:#81a1c1
|
||||||
|
--color=fg+:#e5e9f0,bg+:#434c5e,hl+:#81a1c1
|
||||||
|
--color=info:#eacb8a,prompt:#bf616a,pointer:#b48ead
|
||||||
|
--color=marker:#a3be8b,spinner:#b48ead,header:#a3be8b
|
||||||
|
'
|
||||||
|
|
||||||
|
# Tokyo Night
|
||||||
|
export FZF_DEFAULT_OPTS='
|
||||||
|
--color=fg:#c0caf5,bg:#1a1b26,hl:#bb9af7
|
||||||
|
--color=fg+:#c0caf5,bg+:#292e42,hl+:#7dcfff
|
||||||
|
--color=info:#7aa2f7,prompt:#7dcfff,pointer:#7dcfff
|
||||||
|
--color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
|
||||||
|
'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Layout Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Reverse layout (prompt at top)
|
||||||
|
fzf --reverse
|
||||||
|
|
||||||
|
# Full screen
|
||||||
|
fzf --height=100%
|
||||||
|
|
||||||
|
# 40% of screen
|
||||||
|
fzf --height=40%
|
||||||
|
|
||||||
|
# Border
|
||||||
|
fzf --border
|
||||||
|
fzf --border=rounded
|
||||||
|
fzf --border=sharp
|
||||||
|
|
||||||
|
# Inline info
|
||||||
|
fzf --inline-info
|
||||||
|
|
||||||
|
# No info
|
||||||
|
fzf --no-info
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Limit results
|
||||||
|
fzf --select-1 # Auto-select if only one match
|
||||||
|
fzf --exit-0 # Exit if no match
|
||||||
|
|
||||||
|
# Algorithm
|
||||||
|
fzf --algo=v1 # Faster, less accurate
|
||||||
|
fzf --algo=v2 # Default, balanced
|
||||||
|
|
||||||
|
# History
|
||||||
|
fzf --history=/tmp/fzf-history
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Default command
|
||||||
|
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
|
||||||
|
|
||||||
|
# Default options
|
||||||
|
export FZF_DEFAULT_OPTS='
|
||||||
|
--height 40%
|
||||||
|
--layout=reverse
|
||||||
|
--border
|
||||||
|
--inline-info
|
||||||
|
--preview "bat --color=always --line-range :500 {}"
|
||||||
|
--bind ctrl-/:toggle-preview
|
||||||
|
'
|
||||||
|
|
||||||
|
# ctrl-t command (file widget)
|
||||||
|
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
|
||||||
|
export FZF_CTRL_T_OPTS="
|
||||||
|
--preview 'bat --color=always --line-range :500 {}'
|
||||||
|
--bind 'ctrl-/:toggle-preview'
|
||||||
|
"
|
||||||
|
|
||||||
|
# alt-c command (directory widget)
|
||||||
|
export FZF_ALT_C_COMMAND='fd --type d --hidden --exclude .git'
|
||||||
|
export FZF_ALT_C_OPTS="
|
||||||
|
--preview 'tree -L 1 {}'
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shell Integration
|
||||||
|
|
||||||
|
### Bash/Zsh Key Bindings
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add to ~/.bashrc or ~/.zshrc
|
||||||
|
source /usr/share/doc/fzf/examples/key-bindings.bash # Bash
|
||||||
|
source /usr/share/doc/fzf/examples/key-bindings.zsh # Zsh
|
||||||
|
|
||||||
|
# Or via package manager
|
||||||
|
eval "$(fzf --bash)" # Bash
|
||||||
|
eval "$(fzf --zsh)" # Zsh
|
||||||
|
```
|
||||||
|
|
||||||
|
Default bindings:
|
||||||
|
|
||||||
|
- `ctrl-t`: Paste selected files
|
||||||
|
- `ctrl-r`: Paste from command history
|
||||||
|
- `alt-c`: cd into selected directory
|
||||||
|
|
||||||
|
### Custom Shell Functions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add to ~/.bashrc or ~/.zshrc
|
||||||
|
|
||||||
|
# fh - repeat history
|
||||||
|
fh() {
|
||||||
|
print -z $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | \
|
||||||
|
fzf +s --tac | \
|
||||||
|
sed -E 's/ *[0-9]*\*? *//' | \
|
||||||
|
sed -E 's/\\/\\\\/g')
|
||||||
|
}
|
||||||
|
|
||||||
|
# fbr - checkout git branch
|
||||||
|
fbr() {
|
||||||
|
local branches branch
|
||||||
|
branches=$(git branch -vv) &&
|
||||||
|
branch=$(echo "$branches" | fzf +m) &&
|
||||||
|
git checkout $(echo "$branch" | awk '{print $1}' | sed "s/.* //")
|
||||||
|
}
|
||||||
|
|
||||||
|
# fshow - git commit browser
|
||||||
|
fshow() {
|
||||||
|
git log --graph --color=always \
|
||||||
|
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
|
||||||
|
fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \
|
||||||
|
--bind "ctrl-m:execute:
|
||||||
|
(grep -o '[a-f0-9]\{7\}' | head -1 |
|
||||||
|
xargs -I % sh -c 'git show --color=always % | less -R') << 'FZF-EOF'
|
||||||
|
{}
|
||||||
|
FZF-EOF"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
1. Use fd instead of find for FZF_DEFAULT_COMMAND
|
||||||
|
2. Limit preview size with --line-range
|
||||||
|
3. Use --select-1 and --exit-0 for automation
|
||||||
|
4. Cache results for repeated searches
|
||||||
|
|
||||||
|
### User Experience
|
||||||
|
|
||||||
|
1. Always provide preview when possible
|
||||||
|
2. Use semantic keybindings (ctrl-e for edit)
|
||||||
|
3. Provide helpful --header messages
|
||||||
|
4. Use --multi for batch operations
|
||||||
|
5. Toggle preview with ctrl-/ for large files
|
||||||
|
|
||||||
|
### Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Combine with xargs
|
||||||
|
fd -e py | fzf -m | xargs wc -l
|
||||||
|
|
||||||
|
# Combine with while read
|
||||||
|
fd | fzf -m | while read file; do
|
||||||
|
echo "Processing: $file"
|
||||||
|
# Process file
|
||||||
|
done
|
||||||
|
|
||||||
|
# Combine with command substitution
|
||||||
|
vim $(fzf)
|
||||||
|
cd $(fd -t d | fzf)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Exit code check
|
||||||
|
if result=$(fd | fzf); then
|
||||||
|
echo "Selected: $result"
|
||||||
|
else
|
||||||
|
echo "No selection or cancelled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Multi-select results
|
||||||
|
selected=$(fd | fzf -m)
|
||||||
|
if [[ -n "$selected" ]]; then
|
||||||
|
echo "$selected" | while read file; do
|
||||||
|
process "$file"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
```
|
||||||
656
references/jq-cookbook.md
Normal file
656
references/jq-cookbook.md
Normal file
@@ -0,0 +1,656 @@
|
|||||||
|
# jq - JSON Processing Cookbook
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`jq` is a lightweight command-line JSON processor. It's like `sed` for JSON data.
|
||||||
|
|
||||||
|
## Basic Queries
|
||||||
|
|
||||||
|
### Identity and Pretty Print
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Identity (pretty print)
|
||||||
|
jq . file.json
|
||||||
|
|
||||||
|
# Compact output
|
||||||
|
jq -c . file.json
|
||||||
|
|
||||||
|
# Raw output (no quotes for strings)
|
||||||
|
jq -r . file.json
|
||||||
|
|
||||||
|
# Sort keys
|
||||||
|
jq -S . file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessing Fields
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Top-level field
|
||||||
|
jq '.name' file.json
|
||||||
|
|
||||||
|
# Nested field
|
||||||
|
jq '.user.email' file.json
|
||||||
|
|
||||||
|
# Deeply nested
|
||||||
|
jq '.data.user.profile.settings.theme' file.json
|
||||||
|
|
||||||
|
# Optional fields (null if missing)
|
||||||
|
jq '.user.address?' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array Access
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# First element
|
||||||
|
jq '.[0]' file.json
|
||||||
|
|
||||||
|
# Last element
|
||||||
|
jq '.[-1]' file.json
|
||||||
|
|
||||||
|
# Specific index
|
||||||
|
jq '.[2]' file.json
|
||||||
|
|
||||||
|
# Slice (first 3 elements)
|
||||||
|
jq '.[:3]' file.json
|
||||||
|
|
||||||
|
# Slice (elements 2-5)
|
||||||
|
jq '.[2:5]' file.json
|
||||||
|
|
||||||
|
# All elements
|
||||||
|
jq '.[]' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Iterating
|
||||||
|
|
||||||
|
### Array Iteration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract field from each element
|
||||||
|
jq '.users[].name' file.json
|
||||||
|
|
||||||
|
# Multiple fields
|
||||||
|
jq '.users[] | .name, .email' file.json
|
||||||
|
|
||||||
|
# Nested iteration
|
||||||
|
jq '.departments[].employees[].name' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Object Iteration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All values
|
||||||
|
jq '.[]' file.json
|
||||||
|
|
||||||
|
# All keys
|
||||||
|
jq 'keys' file.json
|
||||||
|
|
||||||
|
# Key-value pairs
|
||||||
|
jq 'to_entries' file.json
|
||||||
|
|
||||||
|
# Iterate key-value
|
||||||
|
jq 'to_entries[] | .key + ": " + (.value | tostring)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filtering
|
||||||
|
|
||||||
|
### Select
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Filter by condition
|
||||||
|
jq '.users[] | select(.active == true)' file.json
|
||||||
|
|
||||||
|
# Multiple conditions (AND)
|
||||||
|
jq '.users[] | select(.active == true and .age > 18)' file.json
|
||||||
|
|
||||||
|
# Multiple conditions (OR)
|
||||||
|
jq '.users[] | select(.active == true or .admin == true)' file.json
|
||||||
|
|
||||||
|
# Not
|
||||||
|
jq '.users[] | select(.active != false)' file.json
|
||||||
|
|
||||||
|
# Check field exists
|
||||||
|
jq '.users[] | select(.email != null)' file.json
|
||||||
|
jq '.users[] | select(has("email"))' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comparison Operators
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Equality
|
||||||
|
jq '.items[] | select(.status == "active")' file.json
|
||||||
|
|
||||||
|
# Greater/Less than
|
||||||
|
jq '.items[] | select(.price > 100)' file.json
|
||||||
|
jq '.items[] | select(.price <= 50)' file.json
|
||||||
|
|
||||||
|
# String contains
|
||||||
|
jq '.items[] | select(.name | contains("widget"))' file.json
|
||||||
|
|
||||||
|
# String starts with
|
||||||
|
jq '.items[] | select(.name | startswith("A"))' file.json
|
||||||
|
|
||||||
|
# String ends with
|
||||||
|
jq '.items[] | select(.name | endswith(".json"))' file.json
|
||||||
|
|
||||||
|
# Regex match
|
||||||
|
jq '.items[] | select(.email | test("@gmail\\.com$"))' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transforming
|
||||||
|
|
||||||
|
### Map
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract single field
|
||||||
|
jq '.users | map(.name)' file.json
|
||||||
|
|
||||||
|
# Extract multiple fields
|
||||||
|
jq '.users | map({name, email})' file.json
|
||||||
|
|
||||||
|
# Transform values
|
||||||
|
jq '.prices | map(. * 1.1)' file.json
|
||||||
|
|
||||||
|
# Conditional transformation
|
||||||
|
jq '.users | map(if .active then .name else empty end)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reduce
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sum
|
||||||
|
jq '[.items[].price] | add' file.json
|
||||||
|
|
||||||
|
# Average
|
||||||
|
jq '[.items[].price] | add / length' file.json
|
||||||
|
|
||||||
|
# Min/Max
|
||||||
|
jq '[.items[].price] | min' file.json
|
||||||
|
jq '[.items[].price] | max' file.json
|
||||||
|
|
||||||
|
# Count
|
||||||
|
jq '.items | length' file.json
|
||||||
|
|
||||||
|
# Custom reduce
|
||||||
|
jq 'reduce .items[] as $item (0; . + $item.price)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Group By
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Group by field
|
||||||
|
jq 'group_by(.category)' file.json
|
||||||
|
|
||||||
|
# Group and count
|
||||||
|
jq 'group_by(.category) | map({category: .[0].category, count: length})' file.json
|
||||||
|
|
||||||
|
# Group and aggregate
|
||||||
|
jq 'group_by(.category) | map({
|
||||||
|
category: .[0].category,
|
||||||
|
total: map(.price) | add
|
||||||
|
})' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sorting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Sort array
|
||||||
|
jq 'sort' file.json
|
||||||
|
|
||||||
|
# Sort by field
|
||||||
|
jq 'sort_by(.name)' file.json
|
||||||
|
|
||||||
|
# Reverse sort
|
||||||
|
jq 'sort_by(.name) | reverse' file.json
|
||||||
|
|
||||||
|
# Sort by multiple fields
|
||||||
|
jq 'sort_by(.category, .name)' file.json
|
||||||
|
|
||||||
|
# Sort numbers
|
||||||
|
jq 'sort_by(.price | tonumber)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constructing
|
||||||
|
|
||||||
|
### Objects
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# New object with specific fields
|
||||||
|
jq '{name: .name, email: .email}' file.json
|
||||||
|
|
||||||
|
# Rename fields
|
||||||
|
jq '{username: .name, mail: .email}' file.json
|
||||||
|
|
||||||
|
# Computed fields
|
||||||
|
jq '{
|
||||||
|
name: .name,
|
||||||
|
fullName: .firstName + " " + .lastName,
|
||||||
|
discountPrice: .price * 0.9
|
||||||
|
}' file.json
|
||||||
|
|
||||||
|
# Nested objects
|
||||||
|
jq '{
|
||||||
|
user: {
|
||||||
|
name: .name,
|
||||||
|
contact: {email: .email}
|
||||||
|
}
|
||||||
|
}' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arrays
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create array from fields
|
||||||
|
jq '[.name, .email, .age]' file.json
|
||||||
|
|
||||||
|
# Array of objects
|
||||||
|
jq '[.users[] | {name, email}]' file.json
|
||||||
|
|
||||||
|
# Flatten nested arrays
|
||||||
|
jq '.departments[].employees | flatten' file.json
|
||||||
|
|
||||||
|
# Unique elements
|
||||||
|
jq '.tags | unique' file.json
|
||||||
|
|
||||||
|
# Array difference
|
||||||
|
jq '.a - .b' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## String Operations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Concatenation
|
||||||
|
jq '.firstName + " " + .lastName' file.json
|
||||||
|
|
||||||
|
# Interpolation
|
||||||
|
jq '"Hello, \(.name)!"' file.json
|
||||||
|
|
||||||
|
# String length
|
||||||
|
jq '.name | length' file.json
|
||||||
|
|
||||||
|
# Uppercase/Lowercase
|
||||||
|
jq '.name | ascii_upcase' file.json
|
||||||
|
jq '.name | ascii_downcase' file.json
|
||||||
|
|
||||||
|
# Split string
|
||||||
|
jq '.path | split("/")' file.json
|
||||||
|
|
||||||
|
# Join array
|
||||||
|
jq '.tags | join(", ")' file.json
|
||||||
|
|
||||||
|
# Trim whitespace
|
||||||
|
jq '.text | gsub("^\\s+|\\s+$"; "")' file.json
|
||||||
|
|
||||||
|
# Replace
|
||||||
|
jq '.text | gsub("old"; "new")' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Conversions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# To string
|
||||||
|
jq '.age | tostring' file.json
|
||||||
|
|
||||||
|
# To number
|
||||||
|
jq '.price | tonumber' file.json
|
||||||
|
|
||||||
|
# To array
|
||||||
|
jq '[.]' file.json
|
||||||
|
|
||||||
|
# Type check
|
||||||
|
jq '.value | type' file.json
|
||||||
|
|
||||||
|
# Type test
|
||||||
|
jq 'if .value | type == "string" then "is string" else "not string" end' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conditionals
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If-then-else
|
||||||
|
jq 'if .active then "active" else "inactive" end' file.json
|
||||||
|
|
||||||
|
# Multiple conditions
|
||||||
|
jq 'if .score >= 90 then "A"
|
||||||
|
elif .score >= 80 then "B"
|
||||||
|
elif .score >= 70 then "C"
|
||||||
|
else "F"
|
||||||
|
end' file.json
|
||||||
|
|
||||||
|
# Ternary-like
|
||||||
|
jq '.users[] | {name, status: (if .active then "active" else "inactive" end)}' file.json
|
||||||
|
|
||||||
|
# Alternative operator
|
||||||
|
jq '.value // "default"' file.json # Use "default" if .value is null/false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Combining Data
|
||||||
|
|
||||||
|
### Merging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Merge objects
|
||||||
|
jq '. + {newField: "value"}' file.json
|
||||||
|
|
||||||
|
# Merge arrays
|
||||||
|
jq '.a + .b' file.json
|
||||||
|
|
||||||
|
# Recursive merge
|
||||||
|
jq '. * {user: {active: true}}' file.json
|
||||||
|
|
||||||
|
# Merge multiple files
|
||||||
|
jq -s '.[0] + .[1]' file1.json file2.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Joining
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Array join
|
||||||
|
jq '.array1 + .array2' file.json
|
||||||
|
|
||||||
|
# Unique elements from multiple arrays
|
||||||
|
jq '(.array1 + .array2) | unique' file.json
|
||||||
|
|
||||||
|
# Intersection
|
||||||
|
jq '.array1 - (.array1 - .array2)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Working with Nested Data
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Recursive descent
|
||||||
|
jq '.. | .name? // empty' file.json
|
||||||
|
|
||||||
|
# Find all values for a key
|
||||||
|
jq '.. | .email? // empty' file.json
|
||||||
|
|
||||||
|
# Flatten deeply nested structure
|
||||||
|
jq '[.. | .users? // empty] | flatten' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Outputs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Multiple queries
|
||||||
|
jq '.name, .email' file.json
|
||||||
|
|
||||||
|
# Conditional multiple outputs
|
||||||
|
jq '.users[] | .name, (if .admin then "ADMIN" else empty end)' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Try-catch
|
||||||
|
jq 'try .field catch "not found"' file.json
|
||||||
|
|
||||||
|
# Optional field access
|
||||||
|
jq '.user?.email?' file.json
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
jq '.count // 0' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Functions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Define and use function
|
||||||
|
jq 'def double: . * 2; .values[] | double' file.json
|
||||||
|
|
||||||
|
# Function with parameters
|
||||||
|
jq 'def multiply(n): . * n; .values[] | multiply(3)' file.json
|
||||||
|
|
||||||
|
# Recursive function
|
||||||
|
jq 'def factorial: if . <= 1 then 1 else . * ((. - 1) | factorial) end; 5 | factorial'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Practical Examples
|
||||||
|
|
||||||
|
### API Response Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract IDs
|
||||||
|
curl -s 'https://api.example.com/users' | jq '.data[].id'
|
||||||
|
|
||||||
|
# Format for CSV
|
||||||
|
curl -s 'https://api.example.com/users' | \
|
||||||
|
jq -r '.data[] | [.id, .name, .email] | @csv'
|
||||||
|
|
||||||
|
# Extract nested data
|
||||||
|
curl -s 'https://api.example.com/posts' | \
|
||||||
|
jq '.posts[] | {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
author: .user.name,
|
||||||
|
comments: .comments | length
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update config value
|
||||||
|
jq '.database.host = "localhost"' config.json
|
||||||
|
|
||||||
|
# Add new field
|
||||||
|
jq '. + {newFeature: true}' config.json
|
||||||
|
|
||||||
|
# Remove field
|
||||||
|
jq 'del(.deprecated)' config.json
|
||||||
|
|
||||||
|
# Merge configs
|
||||||
|
jq -s '.[0] * .[1]' base.json override.json > merged.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Count by category
|
||||||
|
jq 'group_by(.category) | map({category: .[0].category, count: length})' data.json
|
||||||
|
|
||||||
|
# Top 10 by price
|
||||||
|
jq 'sort_by(.price) | reverse | .[:10]' data.json
|
||||||
|
|
||||||
|
# Statistics
|
||||||
|
jq '{
|
||||||
|
total: length,
|
||||||
|
avgPrice: (map(.price) | add / length),
|
||||||
|
maxPrice: (map(.price) | max),
|
||||||
|
minPrice: (map(.price) | min)
|
||||||
|
}' data.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing and Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check required fields
|
||||||
|
jq '.users[] | select(has("name") and has("email") | not)' data.json
|
||||||
|
|
||||||
|
# Validate email format
|
||||||
|
jq '.users[] | select(.email | test("^[^@]+@[^@]+$") | not)' data.json
|
||||||
|
|
||||||
|
# Find duplicates
|
||||||
|
jq 'group_by(.id) | map(select(length > 1))' data.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Filter log level
|
||||||
|
jq 'select(.level == "error")' logs.json
|
||||||
|
|
||||||
|
# Extract error messages
|
||||||
|
jq 'select(.level == "error") | .message' logs.json
|
||||||
|
|
||||||
|
# Group errors by type
|
||||||
|
jq -s 'group_by(.error_type) | map({type: .[0].error_type, count: length})' logs.json
|
||||||
|
|
||||||
|
# Time range filter
|
||||||
|
jq 'select(.timestamp >= "2024-01-01" and .timestamp < "2024-02-01")' logs.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Formats
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Raw output (no quotes)
|
||||||
|
jq -r '.name' file.json
|
||||||
|
|
||||||
|
# Compact output
|
||||||
|
jq -c '.' file.json
|
||||||
|
|
||||||
|
# Tab-separated
|
||||||
|
jq -r '.[] | [.name, .age] | @tsv' file.json
|
||||||
|
|
||||||
|
# CSV
|
||||||
|
jq -r '.[] | [.name, .age, .email] | @csv' file.json
|
||||||
|
|
||||||
|
# URL encoding
|
||||||
|
jq -r '.params | @uri' file.json
|
||||||
|
|
||||||
|
# Base64 encoding
|
||||||
|
jq -r '.data | @base64' file.json
|
||||||
|
|
||||||
|
# HTML encoding
|
||||||
|
jq -r '.content | @html' file.json
|
||||||
|
|
||||||
|
# JSON output (default)
|
||||||
|
jq '.' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Command-Line Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Read from stdin
|
||||||
|
echo '{"name":"test"}' | jq .
|
||||||
|
|
||||||
|
# Multiple files
|
||||||
|
jq . file1.json file2.json
|
||||||
|
|
||||||
|
# Slurp (read all files into array)
|
||||||
|
jq -s . file1.json file2.json
|
||||||
|
|
||||||
|
# Null input (generate data)
|
||||||
|
jq -n '{name: "test", date: now}'
|
||||||
|
|
||||||
|
# Exit with status code
|
||||||
|
jq -e 'select(.status == "ok")' file.json
|
||||||
|
|
||||||
|
# Tab indentation
|
||||||
|
jq --tab . file.json
|
||||||
|
|
||||||
|
# No color
|
||||||
|
jq -M . file.json
|
||||||
|
|
||||||
|
# Color (force)
|
||||||
|
jq -C . file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pretty print
|
||||||
|
jq . file.json | less
|
||||||
|
|
||||||
|
# Inspect structure
|
||||||
|
jq 'paths' file.json
|
||||||
|
|
||||||
|
# Show all keys
|
||||||
|
jq '[paths | select(length == 1)] | unique' file.json
|
||||||
|
|
||||||
|
# Debug filter
|
||||||
|
jq 'debug | .name' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stream processing (memory efficient)
|
||||||
|
jq --stream . large.json
|
||||||
|
|
||||||
|
# Compact input
|
||||||
|
jq -c . file.json
|
||||||
|
|
||||||
|
# Limit output
|
||||||
|
jq 'limit(10; .items[])' file.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combining with Other Tools
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With curl
|
||||||
|
curl -s 'https://api.github.com/users/github' | jq '.name'
|
||||||
|
|
||||||
|
# With fd/rg
|
||||||
|
fd -e json | xargs jq '.version'
|
||||||
|
|
||||||
|
# With fzf
|
||||||
|
jq -r '.users[].name' users.json | fzf
|
||||||
|
|
||||||
|
# With xargs
|
||||||
|
jq -r '.files[]' manifest.json | xargs cat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Filter-Map-Reduce
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Classic pattern
|
||||||
|
jq '.items[] | # iterate
|
||||||
|
select(.active) | # filter
|
||||||
|
.price | # map
|
||||||
|
add' # reduce
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Page 1 (items 0-9)
|
||||||
|
jq '.items[0:10]' data.json
|
||||||
|
|
||||||
|
# Page 2 (items 10-19)
|
||||||
|
jq '.items[10:20]' data.json
|
||||||
|
|
||||||
|
# Function for pagination
|
||||||
|
page() {
|
||||||
|
local page=$1
|
||||||
|
local size=${2:-10}
|
||||||
|
local start=$((page * size))
|
||||||
|
local end=$((start + size))
|
||||||
|
jq ".items[$start:$end]" data.json
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Migration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Old format to new format
|
||||||
|
jq 'map({
|
||||||
|
id: .user_id,
|
||||||
|
profile: {
|
||||||
|
name: .username,
|
||||||
|
email: .user_email
|
||||||
|
}
|
||||||
|
})' old_format.json > new_format.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Messages
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Common errors and fixes
|
||||||
|
|
||||||
|
# "parse error: Invalid numeric literal"
|
||||||
|
# → Check JSON syntax, trailing commas
|
||||||
|
|
||||||
|
# "Cannot iterate over null"
|
||||||
|
# → Add optional operator: .field[]?
|
||||||
|
|
||||||
|
# "Cannot index string with string"
|
||||||
|
# → Check data types, .field might be string not object
|
||||||
|
|
||||||
|
# "jq: error: syntax error"
|
||||||
|
# → Check quote escaping in shell
|
||||||
|
```
|
||||||
443
references/rg-patterns.md
Normal file
443
references/rg-patterns.md
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
# rg - Ripgrep Patterns and Usage
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`ripgrep` (rg) is an extremely fast text search tool that respects `.gitignore` and supports regex patterns.
|
||||||
|
|
||||||
|
## Basic Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Simple search
|
||||||
|
rg 'pattern'
|
||||||
|
|
||||||
|
# Case-insensitive
|
||||||
|
rg -i 'pattern'
|
||||||
|
|
||||||
|
# Case-sensitive (force)
|
||||||
|
rg -s 'Pattern'
|
||||||
|
|
||||||
|
# Smart case (case-insensitive unless uppercase present)
|
||||||
|
rg -S 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Type Filtering
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search in specific file types
|
||||||
|
rg -t py 'import' # Python files
|
||||||
|
rg -t rust 'fn main' # Rust files
|
||||||
|
rg -t js 'function' # JavaScript files
|
||||||
|
|
||||||
|
# Multiple types
|
||||||
|
rg -t py -t js 'class'
|
||||||
|
|
||||||
|
# Exclude types
|
||||||
|
rg -T py 'pattern' # Everything except Python
|
||||||
|
|
||||||
|
# List available types
|
||||||
|
rg --type-list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context Control
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lines before match
|
||||||
|
rg -B 2 'pattern'
|
||||||
|
|
||||||
|
# Lines after match
|
||||||
|
rg -A 5 'pattern'
|
||||||
|
|
||||||
|
# Lines before and after
|
||||||
|
rg -C 3 'pattern'
|
||||||
|
|
||||||
|
# Both (different values)
|
||||||
|
rg -B 2 -A 5 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show line numbers (default in terminal)
|
||||||
|
rg -n 'pattern'
|
||||||
|
|
||||||
|
# Hide line numbers
|
||||||
|
rg -N 'pattern'
|
||||||
|
|
||||||
|
# Show column numbers
|
||||||
|
rg --column 'pattern'
|
||||||
|
|
||||||
|
# Show file paths only
|
||||||
|
rg -l 'pattern'
|
||||||
|
|
||||||
|
# Show files without matches
|
||||||
|
rg --files-without-match 'pattern'
|
||||||
|
|
||||||
|
# Count matches per file
|
||||||
|
rg -c 'pattern'
|
||||||
|
|
||||||
|
# Count total matches
|
||||||
|
rg --count-matches 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Search
|
||||||
|
|
||||||
|
### Word Boundaries
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Match whole words only
|
||||||
|
rg -w 'word'
|
||||||
|
|
||||||
|
# Example: find 'test' but not 'testing'
|
||||||
|
rg -w 'test'
|
||||||
|
|
||||||
|
# Regex word boundaries
|
||||||
|
rg '\bword\b'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiline Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable multiline mode
|
||||||
|
rg -U 'pattern.*spanning.*lines'
|
||||||
|
|
||||||
|
# Example: find function definitions
|
||||||
|
rg -U 'def \w+\([^)]*\):\s*"""[^"]*"""'
|
||||||
|
|
||||||
|
# Multiline with context
|
||||||
|
rg -U -C 2 'pattern.*multiline'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fixed Strings
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Literal string search (no regex, faster)
|
||||||
|
rg -F 'literal.string[with]special.chars'
|
||||||
|
|
||||||
|
# Case-insensitive literal
|
||||||
|
rg -F -i 'Literal String'
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Filtering
|
||||||
|
|
||||||
|
### By Path
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search in specific directory
|
||||||
|
rg 'pattern' src/
|
||||||
|
|
||||||
|
# Multiple directories
|
||||||
|
rg 'pattern' src/ tests/
|
||||||
|
|
||||||
|
# Glob patterns
|
||||||
|
rg -g '*.py' 'pattern'
|
||||||
|
rg -g '**/*test*.py' 'pattern'
|
||||||
|
|
||||||
|
# Multiple globs
|
||||||
|
rg -g '*.{js,ts}' 'pattern'
|
||||||
|
|
||||||
|
# Negative globs (exclude)
|
||||||
|
rg -g '!*test*' 'pattern'
|
||||||
|
rg -g '!*.min.js' 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hidden and Ignored Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search hidden files
|
||||||
|
rg --hidden 'pattern'
|
||||||
|
|
||||||
|
# Ignore .gitignore rules
|
||||||
|
rg --no-ignore 'pattern'
|
||||||
|
|
||||||
|
# Both
|
||||||
|
rg --hidden --no-ignore 'pattern'
|
||||||
|
|
||||||
|
# Search specific ignored directories
|
||||||
|
rg --no-ignore -g 'node_modules/**' 'pattern'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regex Patterns
|
||||||
|
|
||||||
|
### Basic Regex
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Any character
|
||||||
|
rg 'a.c' # matches 'abc', 'a2c', etc.
|
||||||
|
|
||||||
|
# Character classes
|
||||||
|
rg '[0-9]+' # one or more digits
|
||||||
|
rg '[a-zA-Z]+' # letters only
|
||||||
|
rg '[^0-9]' # anything except digits
|
||||||
|
|
||||||
|
# Anchors
|
||||||
|
rg '^pattern' # start of line
|
||||||
|
rg 'pattern$' # end of line
|
||||||
|
rg '^pattern$' # entire line matches
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quantifiers
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Zero or more
|
||||||
|
rg 'ab*c' # ac, abc, abbc, etc.
|
||||||
|
|
||||||
|
# One or more
|
||||||
|
rg 'ab+c' # abc, abbc, etc.
|
||||||
|
|
||||||
|
# Zero or one
|
||||||
|
rg 'ab?c' # ac, abc
|
||||||
|
|
||||||
|
# Exact count
|
||||||
|
rg 'a{3}' # aaa
|
||||||
|
rg 'a{2,4}' # aa, aaa, aaaa
|
||||||
|
rg 'a{2,}' # aa, aaa, aaaa, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Groups and Alternation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Groups
|
||||||
|
rg '(ab)+' # ab, abab, ababab, etc.
|
||||||
|
|
||||||
|
# Alternation
|
||||||
|
rg 'cat|dog' # cat or dog
|
||||||
|
rg '(import|from) numpy'
|
||||||
|
|
||||||
|
# Non-capturing groups
|
||||||
|
rg '(?:http|https)://[^\s]+'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lookahead/Lookbehind
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Positive lookahead
|
||||||
|
rg 'test(?=_unit)' # 'test' followed by '_unit'
|
||||||
|
|
||||||
|
# Negative lookahead
|
||||||
|
rg 'test(?!_integration)' # 'test' not followed by '_integration'
|
||||||
|
|
||||||
|
# Positive lookbehind
|
||||||
|
rg '(?<=def )\\w+' # word after 'def '
|
||||||
|
|
||||||
|
# Negative lookbehind
|
||||||
|
rg '(?<!@)property' # 'property' not preceded by '@'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Replacement (with sed/perl)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find and replace (dry run)
|
||||||
|
rg -l 'old_pattern' | xargs sed -n 's/old_pattern/new_pattern/gp'
|
||||||
|
|
||||||
|
# Find and replace (in-place)
|
||||||
|
rg -l 'old_pattern' | xargs sed -i '' 's/old_pattern/new_pattern/g'
|
||||||
|
|
||||||
|
# Using perl for complex replacements
|
||||||
|
rg -l 'pattern' | xargs perl -pi -e 's/old/new/g'
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# JSON output for scripting
|
||||||
|
rg --json 'pattern'
|
||||||
|
|
||||||
|
# Parse with jq
|
||||||
|
rg --json 'pattern' | jq -r 'select(.type == "match") | .data.path.text'
|
||||||
|
|
||||||
|
# Get line numbers
|
||||||
|
rg --json 'pattern' | jq -r 'select(.type == "match") | "\(.data.path.text):\(.data.line_number)"'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Statistics and Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Show statistics
|
||||||
|
rg --stats 'pattern'
|
||||||
|
|
||||||
|
# Count matches per file
|
||||||
|
rg -c 'pattern' | sort -t: -k2 -nr
|
||||||
|
|
||||||
|
# Find most common matches
|
||||||
|
rg -o 'pattern' | sort | uniq -c | sort -nr
|
||||||
|
|
||||||
|
# Files with most matches
|
||||||
|
rg -c 'TODO' | awk -F: '$2 > 0' | sort -t: -k2 -nr | head
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### Code Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find function definitions (Python)
|
||||||
|
rg 'def \w+\(' -t py
|
||||||
|
|
||||||
|
# Find class definitions
|
||||||
|
rg 'class \w+' -t py
|
||||||
|
|
||||||
|
# Find imports
|
||||||
|
rg 'from .* import' -t py
|
||||||
|
|
||||||
|
# Find TODO/FIXME comments
|
||||||
|
rg '(TODO|FIXME|XXX|HACK):'
|
||||||
|
|
||||||
|
# Find console.log statements
|
||||||
|
rg 'console\.(log|error|warn)' -t js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find API keys (be careful!)
|
||||||
|
rg -i 'api[_-]?key' -g '!*.md'
|
||||||
|
|
||||||
|
# Find environment variables
|
||||||
|
rg 'process\.env\.' -t js
|
||||||
|
rg 'os\.getenv' -t py
|
||||||
|
|
||||||
|
# Find hardcoded IPs
|
||||||
|
rg '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
|
||||||
|
|
||||||
|
# Find URLs
|
||||||
|
rg 'https?://[^\s<>"{}|\\^`\[\]]+'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Analysis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all imports of a module
|
||||||
|
rg 'from mymodule import' -t py
|
||||||
|
|
||||||
|
# Find all requires
|
||||||
|
rg "require\(['\"].*['\"]\\)" -t js
|
||||||
|
|
||||||
|
# Find external dependencies
|
||||||
|
rg 'import.*from ["\'](?!\.)[^"\']+' -t py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find test functions
|
||||||
|
rg 'def test_\w+' -t py
|
||||||
|
rg 'it\(["\']' -t js
|
||||||
|
|
||||||
|
# Find assertions
|
||||||
|
rg 'assert\w*\(' -t py
|
||||||
|
rg 'expect\(' -t js
|
||||||
|
|
||||||
|
# Find skipped tests
|
||||||
|
rg '@skip|@pytest\.mark\.skip' -t py
|
||||||
|
rg '\.skip\(' -t js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
1. **Use file type filters**: `-t` is faster than glob patterns
|
||||||
|
2. **Use fixed strings when possible**: `-F` bypasses regex engine
|
||||||
|
3. **Limit search scope**: Specify directories explicitly
|
||||||
|
4. **Use word boundaries**: `-w` is faster than `\b` regex
|
||||||
|
5. **Respect .gitignore**: Default behavior is already optimized
|
||||||
|
|
||||||
|
## Interactive Workflows
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search and edit
|
||||||
|
rg -l 'TODO' | fzf | xargs $EDITOR
|
||||||
|
|
||||||
|
# Search with preview
|
||||||
|
rg --line-number 'pattern' | fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}'
|
||||||
|
|
||||||
|
# Multi-select and edit
|
||||||
|
rg -l 'pattern' | fzf -m | xargs $EDITOR
|
||||||
|
|
||||||
|
# Search, select, and replace
|
||||||
|
FILE=$(rg -l 'pattern' | fzf)
|
||||||
|
rg 'pattern' "$FILE" | fzf
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Create `~/.ripgreprc` for default options:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Always show line numbers
|
||||||
|
--line-number
|
||||||
|
|
||||||
|
# Smart case search
|
||||||
|
--smart-case
|
||||||
|
|
||||||
|
# Follow symlinks
|
||||||
|
--follow
|
||||||
|
|
||||||
|
# Custom type definitions
|
||||||
|
--type-add=web:*.{html,css,js}*
|
||||||
|
```
|
||||||
|
|
||||||
|
Use with: `export RIPGREP_CONFIG_PATH=~/.ripgreprc`
|
||||||
|
|
||||||
|
## Comparison with grep
|
||||||
|
|
||||||
|
| Task | grep | rg |
|
||||||
|
|------|------|-----|
|
||||||
|
| Case-insensitive | `grep -i` | `rg -i` |
|
||||||
|
| Recursive | `grep -r` | `rg` (default) |
|
||||||
|
| Show line numbers | `grep -n` | `rg -n` (default) |
|
||||||
|
| Context | `grep -C 3` | `rg -C 3` |
|
||||||
|
| File type | `grep --include='*.py'` | `rg -t py` |
|
||||||
|
| Exclude pattern | `grep --exclude='test*'` | `rg -g '!test*'` |
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
### Shell Aliases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias rgi='rg -i' # Case-insensitive
|
||||||
|
alias rga='rg --hidden --no-ignore' # Search everything
|
||||||
|
alias rgl='rg -l' # Files only
|
||||||
|
alias rgc='rg -c' # Count per file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search and edit function
|
||||||
|
rge() {
|
||||||
|
rg -l "$1" | fzf --preview "rg --color=always -C 5 '$1' {}" | xargs $EDITOR
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search in specific file type
|
||||||
|
rgt() {
|
||||||
|
TYPE=$1
|
||||||
|
shift
|
||||||
|
rg -t "$TYPE" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search with context and open
|
||||||
|
rgf() {
|
||||||
|
rg --line-number "$1" | fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}' | awk -F: '{print $1}' | xargs $EDITOR
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complex Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Email addresses
|
||||||
|
rg '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
||||||
|
|
||||||
|
# Phone numbers (US)
|
||||||
|
rg '\b\d{3}[-.]?\d{3}[-.]?\d{4}\b'
|
||||||
|
|
||||||
|
# Hex colors
|
||||||
|
rg '#[0-9a-fA-F]{6}\b'
|
||||||
|
|
||||||
|
# Semantic versions
|
||||||
|
rg '\b\d+\.\d+\.\d+\b'
|
||||||
|
|
||||||
|
# Python function with type hints
|
||||||
|
rg 'def \w+\([^)]*\) -> \w+:'
|
||||||
|
|
||||||
|
# SQL queries in code
|
||||||
|
rg -U 'SELECT.*FROM.*WHERE'
|
||||||
|
```
|
||||||
677
references/yq-examples.md
Normal file
677
references/yq-examples.md
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
# yq - YAML/XML Processing Examples
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`yq` is a lightweight command-line YAML/XML processor. It's like `jq` but for YAML and XML.
|
||||||
|
|
||||||
|
## YAML Processing
|
||||||
|
|
||||||
|
### Basic Queries
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pretty print
|
||||||
|
yq . file.yaml
|
||||||
|
|
||||||
|
# Access field
|
||||||
|
yq '.name' file.yaml
|
||||||
|
|
||||||
|
# Nested access
|
||||||
|
yq '.database.host' file.yaml
|
||||||
|
|
||||||
|
# Array access
|
||||||
|
yq '.items[0]' file.yaml
|
||||||
|
|
||||||
|
# All array elements
|
||||||
|
yq '.items[]' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Filter by condition
|
||||||
|
yq '.users[] | select(.active == true)' file.yaml
|
||||||
|
|
||||||
|
# Multiple conditions
|
||||||
|
yq '.users[] | select(.age > 18 and .active == true)' file.yaml
|
||||||
|
|
||||||
|
# String contains
|
||||||
|
yq '.items[] | select(.name | contains("widget"))' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transforming
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract specific fields
|
||||||
|
yq '.users[] | {name, email}' file.yaml
|
||||||
|
|
||||||
|
# Map array
|
||||||
|
yq '.items | map(.price * 1.1)' file.yaml
|
||||||
|
|
||||||
|
# Sort
|
||||||
|
yq 'sort_by(.name)' file.yaml
|
||||||
|
|
||||||
|
# Group by
|
||||||
|
yq 'group_by(.category)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating Values
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update single field
|
||||||
|
yq '.database.host = "localhost"' file.yaml
|
||||||
|
|
||||||
|
# Update nested field
|
||||||
|
yq '.server.config.port = 8080' file.yaml
|
||||||
|
|
||||||
|
# Update array element
|
||||||
|
yq '.items[0].price = 99.99' file.yaml
|
||||||
|
|
||||||
|
# Add new field
|
||||||
|
yq '. + {"newField": "value"}' file.yaml
|
||||||
|
|
||||||
|
# Delete field
|
||||||
|
yq 'del(.deprecated)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### In-Place Editing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit file in-place
|
||||||
|
yq -i '.version = "2.0"' file.yaml
|
||||||
|
|
||||||
|
# Multiple updates
|
||||||
|
yq -i '
|
||||||
|
.version = "2.0" |
|
||||||
|
.updated = now |
|
||||||
|
.deprecated = null
|
||||||
|
' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Merging Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Merge two YAML files
|
||||||
|
yq '. *= load("other.yaml")' base.yaml
|
||||||
|
|
||||||
|
# Merge with override
|
||||||
|
yq 'load("base.yaml") * load("override.yaml")'
|
||||||
|
|
||||||
|
# Merge multiple files
|
||||||
|
yq eval-all '. as $item ireduce ({}; . * $item)' file1.yaml file2.yaml file3.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Format Conversion
|
||||||
|
|
||||||
|
### YAML to JSON
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert YAML to JSON
|
||||||
|
yq -o json . file.yaml
|
||||||
|
|
||||||
|
# Compact JSON
|
||||||
|
yq -o json -I 0 . file.yaml
|
||||||
|
|
||||||
|
# Pretty JSON
|
||||||
|
yq -o json . file.yaml | jq .
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON to YAML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert JSON to YAML
|
||||||
|
yq -P . file.json
|
||||||
|
|
||||||
|
# From stdin
|
||||||
|
echo '{"name":"test"}' | yq -P .
|
||||||
|
|
||||||
|
# Pipeline
|
||||||
|
cat file.json | yq -P . > file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### YAML to TOML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert YAML to TOML
|
||||||
|
yq -o toml . file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### YAML to XML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert YAML to XML
|
||||||
|
yq -o xml . file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## XML Processing
|
||||||
|
|
||||||
|
### Reading XML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Parse XML
|
||||||
|
yq -p xml . file.xml
|
||||||
|
|
||||||
|
# Access element
|
||||||
|
yq -p xml '.root.element' file.xml
|
||||||
|
|
||||||
|
# Access attribute
|
||||||
|
yq -p xml '.root.+@attr' file.xml
|
||||||
|
|
||||||
|
# All elements
|
||||||
|
yq -p xml '.root.items[]' file.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filtering XML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Filter by attribute
|
||||||
|
yq -p xml '.root.item[] | select(.+@id == "123")' file.xml
|
||||||
|
|
||||||
|
# Filter by element value
|
||||||
|
yq -p xml '.root.item[] | select(.name == "test")' file.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
### XML to JSON
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert XML to JSON
|
||||||
|
yq -p xml -o json . file.xml
|
||||||
|
|
||||||
|
# Extract and convert
|
||||||
|
yq -p xml -o json '.root.items' file.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
### XML to YAML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Convert XML to YAML
|
||||||
|
yq -p xml . file.xml
|
||||||
|
|
||||||
|
# Pretty print
|
||||||
|
yq -p xml -P . file.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Use Cases
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get service names
|
||||||
|
yq '.services | keys' docker-compose.yml
|
||||||
|
|
||||||
|
# Get image for service
|
||||||
|
yq '.services.web.image' docker-compose.yml
|
||||||
|
|
||||||
|
# Update service port
|
||||||
|
yq -i '.services.web.ports[0] = "8080:80"' docker-compose.yml
|
||||||
|
|
||||||
|
# Add environment variable
|
||||||
|
yq -i '.services.web.environment.DEBUG = "true"' docker-compose.yml
|
||||||
|
|
||||||
|
# Get all exposed ports
|
||||||
|
yq '.services[].ports[]' docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get pod name
|
||||||
|
yq '.metadata.name' pod.yaml
|
||||||
|
|
||||||
|
# Get container image
|
||||||
|
yq '.spec.containers[0].image' pod.yaml
|
||||||
|
|
||||||
|
# Update replicas
|
||||||
|
yq -i '.spec.replicas = 3' deployment.yaml
|
||||||
|
|
||||||
|
# Get all container names
|
||||||
|
yq '.spec.template.spec.containers[].name' deployment.yaml
|
||||||
|
|
||||||
|
# Get resource limits
|
||||||
|
yq '.spec.template.spec.containers[0].resources.limits' deployment.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD (GitHub Actions, GitLab CI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get job names
|
||||||
|
yq '.jobs | keys' .github/workflows/ci.yml
|
||||||
|
|
||||||
|
# Get steps for job
|
||||||
|
yq '.jobs.build.steps[].name' .github/workflows/ci.yml
|
||||||
|
|
||||||
|
# Update trigger
|
||||||
|
yq -i '.on.push.branches = ["main", "develop"]' .github/workflows/ci.yml
|
||||||
|
|
||||||
|
# Get all used actions
|
||||||
|
yq '.jobs[].steps[].uses' .github/workflows/ci.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Application config
|
||||||
|
yq '.app.port' config.yaml
|
||||||
|
yq '.database.connection.host' config.yaml
|
||||||
|
|
||||||
|
# Update config
|
||||||
|
yq -i '.app.debug = true' config.yaml
|
||||||
|
yq -i '.cache.enabled = false' config.yaml
|
||||||
|
|
||||||
|
# Merge configs
|
||||||
|
yq '. *= load("config.local.yaml")' config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ansible
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get hosts
|
||||||
|
yq '.all.hosts | keys' inventory.yaml
|
||||||
|
|
||||||
|
# Get variables
|
||||||
|
yq '.all.vars' inventory.yaml
|
||||||
|
|
||||||
|
# Update host variable
|
||||||
|
yq -i '.all.hosts.server1.ansible_host = "192.168.1.10"' inventory.yaml
|
||||||
|
|
||||||
|
# Get all playbook tasks
|
||||||
|
yq '.[] | select(.tasks) | .tasks[].name' playbook.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Patterns
|
||||||
|
|
||||||
|
### Conditional Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update if exists
|
||||||
|
yq '(select(.field) | .field) = "new-value"' file.yaml
|
||||||
|
|
||||||
|
# Update based on condition
|
||||||
|
yq '(.items[] | select(.price > 100) | .discount) = 0.2' file.yaml
|
||||||
|
|
||||||
|
# Add field if missing
|
||||||
|
yq '. + (if has("field") then {} else {"field": "default"} end)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Complex Transformations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Restructure data
|
||||||
|
yq '.users | map({
|
||||||
|
username: .name,
|
||||||
|
contact: {
|
||||||
|
email: .email,
|
||||||
|
phone: .phone
|
||||||
|
}
|
||||||
|
})' file.yaml
|
||||||
|
|
||||||
|
# Aggregate data
|
||||||
|
yq '.items | group_by(.category) | map({
|
||||||
|
category: .[0].category,
|
||||||
|
total: map(.price) | add
|
||||||
|
})' file.yaml
|
||||||
|
|
||||||
|
# Pivot data
|
||||||
|
yq 'reduce .items[] as $item ({}; .[$item.name] = $item.value)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Arrays
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Append to array
|
||||||
|
yq '.items += ["new-item"]' file.yaml
|
||||||
|
|
||||||
|
# Prepend to array
|
||||||
|
yq '.items = ["new-item"] + .items' file.yaml
|
||||||
|
|
||||||
|
# Remove from array
|
||||||
|
yq 'del(.items[2])' file.yaml
|
||||||
|
|
||||||
|
# Filter array
|
||||||
|
yq '.items = [.items[] | select(.active == true)]' file.yaml
|
||||||
|
|
||||||
|
# Unique array
|
||||||
|
yq '.items | unique' file.yaml
|
||||||
|
|
||||||
|
# Flatten array
|
||||||
|
yq '.items | flatten' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Document YAML
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Process all documents
|
||||||
|
yq '.' multi-doc.yaml
|
||||||
|
|
||||||
|
# Select specific document
|
||||||
|
yq 'select(document_index == 0)' multi-doc.yaml
|
||||||
|
|
||||||
|
# Filter documents
|
||||||
|
yq 'select(.kind == "Deployment")' multi-doc.yaml
|
||||||
|
|
||||||
|
# Update all documents
|
||||||
|
yq -i '.metadata.labels.app = "myapp"' multi-doc.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## String Operations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Concatenation
|
||||||
|
yq '.fullName = .firstName + " " + .lastName' file.yaml
|
||||||
|
|
||||||
|
# Interpolation
|
||||||
|
yq '.message = "Hello, " + .name + "!"' file.yaml
|
||||||
|
|
||||||
|
# Uppercase
|
||||||
|
yq '.name | upcase' file.yaml
|
||||||
|
|
||||||
|
# Lowercase
|
||||||
|
yq '.name | downcase' file.yaml
|
||||||
|
|
||||||
|
# Split
|
||||||
|
yq '.path | split("/")' file.yaml
|
||||||
|
|
||||||
|
# Join
|
||||||
|
yq '.items | join(", ")' file.yaml
|
||||||
|
|
||||||
|
# Replace
|
||||||
|
yq '.text | sub("old", "new")' file.yaml
|
||||||
|
|
||||||
|
# Trim
|
||||||
|
yq '.text | trim' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Checking and Conversion
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check type
|
||||||
|
yq '.field | type' file.yaml
|
||||||
|
|
||||||
|
# Convert to string
|
||||||
|
yq '.number | tostring' file.yaml
|
||||||
|
|
||||||
|
# Convert to number
|
||||||
|
yq '.string | tonumber' file.yaml
|
||||||
|
|
||||||
|
# Check if exists
|
||||||
|
yq 'has("field")' file.yaml
|
||||||
|
|
||||||
|
# Length
|
||||||
|
yq '.items | length' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preserve comments (default behavior)
|
||||||
|
yq '.field = "new-value"' file.yaml
|
||||||
|
|
||||||
|
# Add headComment
|
||||||
|
yq '.field headComment="This is a comment"' file.yaml
|
||||||
|
|
||||||
|
# Add lineComment
|
||||||
|
yq '.field lineComment="inline comment"' file.yaml
|
||||||
|
|
||||||
|
# Add footComment
|
||||||
|
yq '.field footComment="bottom comment"' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anchors and Aliases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create anchor
|
||||||
|
yq '.base.&anchor = {"key": "value"}' file.yaml
|
||||||
|
|
||||||
|
# Reference anchor
|
||||||
|
yq '.other = .base.*anchor' file.yaml
|
||||||
|
|
||||||
|
# Merge anchors
|
||||||
|
yq '.combined = .base.*anchor * .override' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if valid YAML
|
||||||
|
yq . file.yaml > /dev/null && echo "Valid" || echo "Invalid"
|
||||||
|
|
||||||
|
# Validate schema (requires schema file)
|
||||||
|
yq 'load("schema.yaml") | . as $schema | load("data.yaml") | validate($schema)'
|
||||||
|
|
||||||
|
# Check required fields
|
||||||
|
yq 'select(has("requiredField") | not)' file.yaml
|
||||||
|
|
||||||
|
# Validate format
|
||||||
|
yq 'select(.email | test("^[^@]+@[^@]+$") | not)' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Formatting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Default YAML output
|
||||||
|
yq . file.yaml
|
||||||
|
|
||||||
|
# Compact (minimal whitespace)
|
||||||
|
yq -I 0 . file.yaml
|
||||||
|
|
||||||
|
# Custom indentation (4 spaces)
|
||||||
|
yq -I 4 . file.yaml
|
||||||
|
|
||||||
|
# No colors
|
||||||
|
yq -C 0 . file.yaml
|
||||||
|
|
||||||
|
# Force colors
|
||||||
|
yq -C 1 . file.yaml
|
||||||
|
|
||||||
|
# Raw output (no quotes)
|
||||||
|
yq -r '.name' file.yaml
|
||||||
|
|
||||||
|
# One line per document
|
||||||
|
yq -o json . multi-doc.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scripting Examples
|
||||||
|
|
||||||
|
### Environment-Specific Configs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
ENV=${1:-dev}
|
||||||
|
|
||||||
|
yq ". *= load(\"config.$ENV.yaml\")" config.base.yaml > config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bulk Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
for file in *.yaml; do
|
||||||
|
yq -i '.metadata.labels.version = "2.0"' "$file"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config Validation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
REQUIRED_FIELDS=("name" "version" "description")
|
||||||
|
|
||||||
|
for field in "${REQUIRED_FIELDS[@]}"; do
|
||||||
|
if ! yq -e "has(\"$field\")" config.yaml > /dev/null; then
|
||||||
|
echo "Missing required field: $field"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secret Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# Extract secrets from YAML
|
||||||
|
yq '.secrets' config.yaml > secrets.yaml
|
||||||
|
|
||||||
|
# Encrypt secrets (example with age)
|
||||||
|
yq '.secrets' config.yaml | age -e -o secrets.age
|
||||||
|
|
||||||
|
# Decrypt and merge
|
||||||
|
age -d secrets.age | yq -P . > secrets.yaml
|
||||||
|
yq '. *= load("secrets.yaml")' config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Examples
|
||||||
|
|
||||||
|
### With Git
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pre-commit hook to sort keys
|
||||||
|
git diff --cached --name-only | \
|
||||||
|
grep '\.yaml$' | \
|
||||||
|
xargs -I {} yq -i -P 'sort_keys(..)' {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extract image tags
|
||||||
|
yq '.services[].image' docker-compose.yml | \
|
||||||
|
cut -d: -f2 | \
|
||||||
|
sort -u
|
||||||
|
|
||||||
|
# Update all images to latest
|
||||||
|
yq -i '(.services[].image) |= sub(":.*", ":latest")' docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Kubernetes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Apply with modifications
|
||||||
|
yq '.spec.replicas = 3' deployment.yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# Extract all images
|
||||||
|
kubectl get pods -o yaml | \
|
||||||
|
yq '.items[].spec.containers[].image' | \
|
||||||
|
sort -u
|
||||||
|
|
||||||
|
# Generate manifests
|
||||||
|
yq eval-all '. as $item ireduce ({}; . * $item)' \
|
||||||
|
base/*.yaml overlays/prod/*.yaml | \
|
||||||
|
kubectl apply -f -
|
||||||
|
```
|
||||||
|
|
||||||
|
### With CI/CD
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update version in workflow
|
||||||
|
VERSION=$(git describe --tags)
|
||||||
|
yq -i ".env.VERSION = \"$VERSION\"" .github/workflows/deploy.yml
|
||||||
|
|
||||||
|
# Extract job matrix
|
||||||
|
MATRIX=$(yq -o json '.jobs.test.strategy.matrix' .github/workflows/ci.yml)
|
||||||
|
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
1. **Use in-place editing**: `-i` avoids reading/writing twice
|
||||||
|
2. **Pipe instead of temp files**: More efficient for transformations
|
||||||
|
3. **Specific paths**: Access `.field.subfield` instead of iterating
|
||||||
|
4. **Limit output**: Use `select` early to filter data
|
||||||
|
5. **Batch operations**: Combine multiple updates in one command
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Configuration Merging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Base + environment + local
|
||||||
|
yq eval-all '. as $item ireduce ({}; . * $item)' \
|
||||||
|
config.base.yaml \
|
||||||
|
config.$ENV.yaml \
|
||||||
|
config.local.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Secret Injection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Replace placeholders with actual values
|
||||||
|
yq '(.. | select(tag == "!!str")) |= envsubst' config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manifest Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate K8s manifests from template
|
||||||
|
yq '(.metadata.name) = "app-" + env(ENV) |
|
||||||
|
(.spec.replicas) = env(REPLICAS) |
|
||||||
|
(.spec.template.spec.containers[0].image) = env(IMAGE)' \
|
||||||
|
template.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pretty print to see structure
|
||||||
|
yq -P . file.yaml
|
||||||
|
|
||||||
|
# Show paths
|
||||||
|
yq '.. | path | join(".")' file.yaml
|
||||||
|
|
||||||
|
# Find all keys
|
||||||
|
yq '.. | select(tag == "!!map") | keys' file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Large Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stream processing
|
||||||
|
yq -N . large.yaml # Process without loading entirely
|
||||||
|
|
||||||
|
# Extract specific document
|
||||||
|
yq 'select(document_index == 5)' large-multi-doc.yaml
|
||||||
|
|
||||||
|
# Count documents
|
||||||
|
yq -N '.' multi-doc.yaml | grep -c '^---$'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shell Aliases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias yqp='yq -P' # Pretty print
|
||||||
|
alias yqj='yq -o json' # To JSON
|
||||||
|
alias yqs='yq -I 2' # 2-space indent
|
||||||
|
alias yqc='yq -C 1' # Force colors
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if file exists and is valid
|
||||||
|
if yq . file.yaml &>/dev/null; then
|
||||||
|
echo "Valid YAML"
|
||||||
|
else
|
||||||
|
echo "Invalid YAML or file not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try with fallback
|
||||||
|
VALUE=$(yq '.field // "default"' file.yaml)
|
||||||
|
|
||||||
|
# Conditional processing
|
||||||
|
if yq -e '.enabled == true' config.yaml > /dev/null; then
|
||||||
|
# Process when enabled
|
||||||
|
yq '.items[]' config.yaml
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comparison with Other Tools
|
||||||
|
|
||||||
|
| Task | yq | jq (JSON) | sed/awk |
|
||||||
|
|------|----|-----------| --------|
|
||||||
|
| Parse YAML | Native | Need conversion | Complex |
|
||||||
|
| Parse JSON | Can do | Native | Possible |
|
||||||
|
| Update in-place | `-i` flag | Needs sponge | `-i` flag |
|
||||||
|
| Preserve comments | Yes | N/A | Yes |
|
||||||
|
| Type safety | Yes | Yes | No |
|
||||||
|
| Learning curve | Medium | Medium | Low/High |
|
||||||
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