Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ The `crepe` binary will be installed to `~/.cargo/bin/`.
### Quick test without installing

```bash
cargo run -- [OPTIONS] PATTERN [FILE...]
cargo run -- [OPTIONS] -e PATTERN [FILE...]
```

## Usage
Expand All @@ -65,57 +65,57 @@ cargo run -- [OPTIONS] PATTERN [FILE...]

```bash
# Highlight all occurrences of "error" in a file
crepe "error" app.log
crepe -e "error" app.log

# Case-insensitive search
crepe -i "warning" app.log
crepe -i -e "warning" app.log

# Multiple patterns with different colors
crepe -e TODO -e FIXME -e HACK src/

# Search recursively through a directory
crepe -r "function" src/
crepe -r -e "function" src/
```

### Advanced Examples

```bash
# Show line numbers and 2 lines of context
crepe -n -C 2 "fn main" src/
crepe -n -C 2 -e "fn main" src/

# Count matches in all Rust files
crepe -r -c --include "*.rs" "struct" .
crepe -r -c --include "*.rs" -e "struct" .

# Search hidden files, exclude node_modules
crepe -r --hidden --exclude "node_modules/*" "import" .
crepe -r --hidden --exclude "node_modules/*" -e "import" .

# Only show the matched parts (not full lines)
crepe -o "\d+\.\d+\.\d+" CHANGELOG.md
crepe -o -e "\d+\.\d+\.\d+" CHANGELOG.md

# Show lines that DON'T match the pattern
crepe -v "debug" app.log
crepe -v -e "debug" app.log

# Match whole words only
crepe -w "cat" file.txt # matches "cat" but not "concatenate"
crepe -w -e "cat" file.txt # matches "cat" but not "concatenate"

# Limit to first 10 matches
crepe -m 10 "error" large-file.log
crepe -m 10 -e "error" large-file.log
```

### Regex Examples

```bash
# Email addresses
crepe "\w+@\w+\.\w+" contacts.txt
crepe -e "\w+@\w+\.\w+" contacts.txt

# IP addresses
crepe "\d+\.\d+\.\d+\.\d+" server.log
crepe -e "\d+\.\d+\.\d+\.\d+" server.log

# Function definitions in JavaScript
crepe "function \w+\(" src/*.js
crepe -e "function \w+\(" src/*.js

# Hexadecimal colors
crepe "#[0-9a-fA-F]{6}" styles.css
crepe -e "#[0-9a-fA-F]{6}" styles.css
```

## Command-Line Options
Expand Down Expand Up @@ -207,7 +207,7 @@ After 12 patterns, colors cycle back to the beginning.
cargo test

# Run with debug output
cargo run -- pattern file
cargo run -- -e pattern file

# Build release version
cargo build --release
Expand Down Expand Up @@ -236,13 +236,13 @@ crepe -r -n -e TODO -e FIXME -e HACK src/
### Configuration Review
```bash
# Find all uncommented lines in a config file
crepe -v "^#" config.conf
crepe -v -e "^#" config.conf
```

### Data Extraction
```bash
# Extract all URLs from a file
crepe -o "https?://[^\s]+" document.txt
crepe -o -e "https?://[^\s]+" document.txt
```

## Built By
Expand Down
116 changes: 45 additions & 71 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ use crate::config::{ColorMode, Config};
#[command(name = "crepe")]
#[command(author, version, about, long_about = None)]
pub struct Cli {
/// Pattern to search for (regex)
#[arg(value_name = "PATTERN")]
pub pattern: Option<String>,
/// Pattern to search for (regex) - use -e for each pattern
#[arg(short = 'e', long = "regexp", value_name = "PATTERN", required = true)]
pub patterns: Vec<String>,

/// Files to search (or stdin if not specified)
#[arg(value_name = "FILE")]
pub files: Vec<PathBuf>,

/// Additional patterns to search for
#[arg(short = 'e', long = "regexp", value_name = "PATTERN")]
pub patterns: Vec<String>,

/// Case-insensitive matching
#[arg(short = 'i', long = "ignore-case")]
pub ignore_case: bool,
Expand Down Expand Up @@ -98,10 +94,6 @@ pub struct Cli {
#[arg(long = "no-filename")]
pub no_filename: bool,

/// Group output by filename
#[arg(long = "heading")]
pub heading: bool,

/// Stop after NUM matches
#[arg(short = 'm', long = "max-count", value_name = "NUM")]
pub max_count: Option<usize>,
Expand All @@ -126,64 +118,9 @@ pub struct Cli {
impl Cli {
/// Convert CLI arguments to Config
pub fn into_config(self) -> Result<Config, String> {
let mut config = Config::default();

// Collect patterns
if let Some(pattern) = self.pattern {
config.patterns.push(pattern);
}
config.patterns.extend(self.patterns);

if config.patterns.is_empty() {
return Err("No pattern specified. Use PATTERN or -e PATTERN.".to_string());
}

// Files
config.files = self.files;

// Pattern matching options
config.case_insensitive = self.ignore_case;
config.whole_words = self.word_regexp;
config.invert_match = self.invert_match;

// Display options
config.show_line_numbers = self.line_number;
config.only_matching = self.only_matching;
config.count_only = self.count;

// Context
if let Some(context) = self.context {
config.context_before = context;
config.context_after = context;
} else {
config.context_before = self.before_context;
config.context_after = self.after_context;
}

// File handling
config.recursive = self.recursive;
config.follow_symlinks = self.follow;
config.hidden = self.hidden;
config.max_depth = self.max_depth;

// File filtering
config.include_patterns = self.include;
config.exclude_patterns = self.exclude;

// Output
config.with_filename = self.with_filename;
config.no_filename = self.no_filename;
config.heading_mode = self.heading;

// Performance
config.max_count = self.max_count;
config.jobs = self.jobs;
config.binary = self.binary;

// Colors
config.no_color = self.no_color;
if let Some(color_when) = self.color {
config.color_mode = match color_when.to_lowercase().as_str() {
// Parse color mode
let color_mode = if let Some(color_when) = self.color {
match color_when.to_lowercase().as_str() {
"always" => ColorMode::Always,
"never" => ColorMode::Never,
"auto" => ColorMode::Auto,
Expand All @@ -193,8 +130,45 @@ impl Cli {
color_when
));
}
};
}
}
} else {
ColorMode::Auto
};

// Determine context values
let (context_before, context_after) = if let Some(context) = self.context {
(context, context)
} else {
(self.before_context, self.after_context)
};

// Create config with all fields initialized
let mut config = Config {
patterns: self.patterns,
files: self.files,
case_insensitive: self.ignore_case,
whole_words: self.word_regexp,
invert_match: self.invert_match,
show_line_numbers: self.line_number,
only_matching: self.only_matching,
count_only: self.count,
context_before,
context_after,
recursive: self.recursive,
follow_symlinks: self.follow,
hidden: self.hidden,
max_depth: self.max_depth,
include_patterns: self.include,
exclude_patterns: self.exclude,
with_filename: self.with_filename,
no_filename: self.no_filename,
max_count: self.max_count,
jobs: self.jobs,
binary: self.binary,
no_color: self.no_color,
color_mode,
..Default::default()
};

// Auto-detect filename display
config.auto_detect_filename_display();
Expand Down
2 changes: 0 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ pub struct Config {
pub show_filename: bool,
pub with_filename: bool,
pub no_filename: bool,
pub heading_mode: bool,

// Performance
pub max_count: Option<usize>,
Expand Down Expand Up @@ -74,7 +73,6 @@ impl Default for Config {
show_filename: false,
with_filename: false,
no_filename: false,
heading_mode: false,
max_count: None,
jobs: None,
binary: false,
Expand Down