diff --git a/copyrat.tmux b/copyrat.tmux index d6b3f1b..08c5c1b 100755 --- a/copyrat.tmux +++ b/copyrat.tmux @@ -53,13 +53,11 @@ tmux bind-key ${keyswitch} -T ${keytable} setup_binding() { local key=$1 - local pattern_name=$2 + local pattern_name="$2" local window_name=$(tmux show-option -gqv @copyrat-window-name) - tmux bind-key -T ${keytable} $key new-window -d -n ${window_name} "${BINARY} --window-name ${window_name} --reverse --unique" + tmux bind-key -T ${keytable} $key new-window -d -n ${window_name} "${BINARY} --window-name ${window_name} --reverse --unique ${pattern_name}" } -# prefix + t + Space searches for all known regexes (noisy and slower) -setup_pattern_binding "space" "" # prefix + t + p searches for absolute & relative paths setup_pattern_binding "p" "--pattern-name path" # prefix + t + u searches for URLs @@ -84,8 +82,12 @@ setup_pattern_binding "m" "--pattern-name mem-address" setup_pattern_binding "4" "--pattern-name ipv4" # prefix + t + 6 searches for IPV6 setup_pattern_binding "6" "--pattern-name ipv6" +# prefix + t + Space searches for all known patterns (noisy and potentially slower) +setup_pattern_binding "space" "--all-patterns" + +# prefix + t + / prompts for a pattern and search for it +tmux bind-key -T ${keytable} "/" command-prompt -p "search:" 'new-window -d -n ${COPYRAT_WINDOW_NAME} "${BINARY} --window-name ${COPYRAT_WINDOW_NAME} --reverse --unique --custom-pattern %%"' -# tmux bind-key -T ${keytable} "/" command-prompt "search:" new-window -d -n ${COPYRAT_WINDOW_NAME} "${BINARY} --window-name ${COPYRAT_WINDOW_NAME} --reverse --unique --custom-regex '%%'" # Auto-install is currently disabled as it requires the user to have cargo installed. # if [ ! -f "$BINARY" ]; then diff --git a/src/lib.rs b/src/lib.rs index 742ba30..9faf269 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,8 +20,9 @@ pub fn run(buffer: String, opt: &CliOpt) -> Option<(String, bool)> { let mut model = model::Model::new( &buffer, &opt.alphabet, - &opt.named_pattern, - &opt.custom_regex, + opt.use_all_patterns, + &opt.named_patterns, + &opt.custom_patterns, opt.reverse, ); @@ -71,13 +72,17 @@ pub struct CliOpt { parse(try_from_str = alphabets::parse_alphabet))] alphabet: alphabets::Alphabet, - /// Pattern names to use (all if not specified). - #[clap(short = "x", long = "--pattern-name", parse(try_from_str = regexes::parse_pattern_name))] - named_pattern: Vec, + /// Use all available regex patterns. + #[clap(short = "A", long = "--all-patterns")] + use_all_patterns: bool, - /// Additional regex patterns. - #[clap(short = "X", long)] - custom_regex: Vec, + /// Pattern names to use ("email", ... see doc). + #[clap(short = "x", long = "--pattern-name", parse(try_from_str = regexes::parse_pattern_name))] + named_patterns: Vec, + + /// Additional regex patterns ("foo*bar", etc). + #[clap(short = "X", long = "--custom-pattern")] + custom_patterns: Vec, /// Assign hints starting from the bottom of the screen. #[clap(short, long)] @@ -91,7 +96,7 @@ pub struct CliOpt { colors: ui::UiColors, /// Align hint with its match. - #[clap(short = "a", long, arg_enum, default_value = "leading")] + #[clap(long, arg_enum, default_value = "leading")] hint_alignment: ui::HintAlignment, /// Move focus back to first/last match. @@ -166,8 +171,10 @@ impl CliOpt { "@copyrat-alphabet" => { self.alphabet = alphabets::parse_alphabet(value)?; } - "@copyrat-regex-id" => (), // TODO - "@copyrat-custom-regex" => self.custom_regex = vec![String::from(value)], + "@copyrat-pattern-name" => { + self.named_patterns = vec![regexes::parse_pattern_name(value)?] + } + "@copyrat-custom-pattern" => self.custom_patterns = vec![String::from(value)], "@copyrat-reverse" => { self.reverse = value.parse::()?; } diff --git a/src/model.rs b/src/model.rs index 7297ae6..8d4d283 100644 --- a/src/model.rs +++ b/src/model.rs @@ -11,8 +11,9 @@ pub struct Model<'a> { // buffer: &'a str, pub lines: Vec<&'a str>, alphabet: &'a Alphabet, + use_all_patterns: bool, named_patterns: &'a Vec, - custom_regexes: &'a Vec, + custom_patterns: &'a Vec, pub reverse: bool, } @@ -20,8 +21,9 @@ impl<'a> Model<'a> { pub fn new( buffer: &'a str, alphabet: &'a Alphabet, + use_all_patterns: bool, named_patterns: &'a Vec, - custom_regexes: &'a Vec, + custom_patterns: &'a Vec, reverse: bool, ) -> Model<'a> { let lines = buffer.split('\n').collect(); @@ -30,8 +32,9 @@ impl<'a> Model<'a> { // buffer, lines, alphabet, + use_all_patterns, named_patterns, - custom_regexes, + custom_patterns, reverse, } } @@ -73,7 +76,7 @@ impl<'a> Model<'a> { .collect::>(); let custom_regexes = self - .custom_regexes + .custom_patterns .iter() .map(|pattern| { ( @@ -83,7 +86,7 @@ impl<'a> Model<'a> { }) .collect::>(); - let regexes = if self.named_patterns.is_empty() { + let regexes = if self.use_all_patterns { PATTERNS .iter() .map(|&(name, pattern)| (name, Regex::new(pattern).unwrap())) @@ -252,10 +255,20 @@ mod tests { #[test] fn match_reverse() { let buffer = "lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 3); assert_eq!(results.first().unwrap().hint, "a"); @@ -265,10 +278,20 @@ mod tests { #[test] fn match_unique() { let buffer = "lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(true); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(true); assert_eq!(results.len(), 3); assert_eq!(results.first().unwrap().hint, "a"); @@ -278,10 +301,20 @@ mod tests { #[test] fn match_docker() { let buffer = "latest sha256:30557a29d5abc51e5f1d5b472e79b7e296f595abcf19fe6b9199dbbc809c6ff4 20 hours ago"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); assert_eq!( @@ -293,10 +326,20 @@ mod tests { #[test] fn match_ansi_colors() { let buffer = "path: /var/log/nginx.log\npath: test/log/nginx-2.log:32folder/.nginx@4df2.log"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = true; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 3); assert_eq!(results.get(0).unwrap().text, "/var/log/nginx.log"); @@ -307,10 +350,20 @@ mod tests { #[test] fn match_paths() { let buffer = "Lorem /tmp/foo/bar_lol, lorem\n Lorem /var/log/boot-strap.log lorem ../log/kern.log lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 3); assert_eq!(results.get(0).unwrap().text, "/tmp/foo/bar_lol"); @@ -321,10 +374,20 @@ mod tests { #[test] fn match_home() { let buffer = "Lorem ~/.gnu/.config.txt, lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); assert_eq!(results.get(0).unwrap().text, "~/.gnu/.config.txt"); @@ -333,10 +396,20 @@ mod tests { #[test] fn match_uuids() { let buffer = "Lorem ipsum 123e4567-e89b-12d3-a456-426655440000 lorem\n Lorem lorem lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); } @@ -344,10 +417,20 @@ mod tests { #[test] fn match_shas() { let buffer = "Lorem fd70b5695 5246ddf f924213 lorem\n Lorem 973113963b491874ab2e372ee60d4b4cb75f717c lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 4); assert_eq!(results.get(0).unwrap().text, "fd70b5695"); @@ -362,10 +445,20 @@ mod tests { #[test] fn match_ipv4s() { let buffer = "Lorem ipsum 127.0.0.1 lorem\n Lorem 255.255.10.255 lorem 127.0.0.1 lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 3); assert_eq!(results.get(0).unwrap().pattern, "ipv4"); @@ -379,10 +472,20 @@ mod tests { #[test] fn match_ipv6s() { let buffer = "Lorem ipsum fe80::2:202:fe4 lorem\n Lorem 2001:67c:670:202:7ba8:5e41:1591:d723 lorem fe80::2:1 lorem ipsum fe80:22:312:fe::1%eth0"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 4); assert_eq!(results.get(0).unwrap().text, "fe80::2:202:fe4"); @@ -398,10 +501,20 @@ mod tests { fn match_markdown_urls() { let buffer = "Lorem ipsum [link](https://github.io?foo=bar) ![](http://cdn.com/img.jpg) lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 2); assert_eq!(results.get(0).unwrap().pattern, "markdown-url"); @@ -413,10 +526,20 @@ mod tests { #[test] fn match_urls() { let buffer = "Lorem ipsum https://www.rust-lang.org/tools lorem\n Lorem ipsumhttps://crates.io lorem https://github.io?foo=bar lorem ssh://github.io"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 4); assert_eq!( @@ -436,10 +559,20 @@ mod tests { fn match_emails() { let buffer = "Lorem ipsum john@server.department.company.com lorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 2); assert_eq!(results.get(0).unwrap().pattern, "email"); @@ -457,10 +590,20 @@ mod tests { #[test] fn match_addresses() { let buffer = "Lorem 0xfd70b5695 0x5246ddf lorem\n Lorem 0x973113tlorem"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 3); assert_eq!(results.get(0).unwrap().pattern, "mem-address"); @@ -474,10 +617,20 @@ mod tests { #[test] fn match_hex_colors() { let buffer = "Lorem #fd7b56 lorem #FF00FF\n Lorem #00fF05 lorem #abcd00 lorem #afRR00"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 4); assert_eq!(results.get(0).unwrap().text, "#fd7b56"); @@ -489,10 +642,20 @@ mod tests { #[test] fn match_ipfs() { let buffer = "Lorem QmRdbNSxDJBXmssAc9fvTtux4duptMvfSGiGuq6yHAQVKQ lorem Qmfoobar"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); assert_eq!( @@ -504,10 +667,20 @@ mod tests { #[test] fn match_process_port() { let buffer = "Lorem 5695 52463 lorem\n Lorem 973113 lorem 99999 lorem 8888 lorem\n 23456 lorem 5432 lorem 23444"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 8); } @@ -515,10 +688,20 @@ mod tests { #[test] fn match_diff_a() { let buffer = "Lorem lorem\n--- a/src/main.rs"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); assert_eq!(results.get(0).unwrap().pattern, "diff-a"); @@ -528,10 +711,20 @@ mod tests { #[test] fn match_diff_b() { let buffer = "Lorem lorem\n+++ b/src/main.rs"; + let use_all_patterns = true; let named_pat = vec![]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 1); assert_eq!(results.get(0).unwrap().pattern, "diff-b"); @@ -539,16 +732,25 @@ mod tests { } #[test] - fn priority() { + fn priority_between_regexes() { let buffer = "Lorem [link](http://foo.bar) ipsum CUSTOM-52463 lorem ISSUE-123 lorem\nLorem /var/fd70b569/9999.log 52463 lorem\n Lorem 973113 lorem 123e4567-e89b-12d3-a456-426655440000 lorem 8888 lorem\n https://crates.io/23456/fd70b569 lorem"; - + let use_all_patterns = true; let named_pat = vec![]; let custom: Vec = ["CUSTOM-[0-9]{4,}", "ISSUE-[0-9]{3}"] .iter() .map(|&s| s.to_string()) .collect(); let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 9); assert_eq!(results.get(0).unwrap().text, "http://foo.bar"); @@ -572,12 +774,22 @@ mod tests { fn named_patterns() { let buffer = "Lorem [link](http://foo.bar) ipsum CUSTOM-52463 lorem ISSUE-123 lorem\nLorem /var/fd70b569/9999.log 52463 lorem\n Lorem 973113 lorem 123e4567-e89b-12d3-a456-426655440000 lorem 8888 lorem\n https://crates.io/23456/fd70b569 lorem"; + let use_all_patterns = false; use crate::regexes::parse_pattern_name; let named_pat = vec![parse_pattern_name("url").unwrap()]; let custom = vec![]; let alphabet = Alphabet("abcd".to_string()); - let results = Model::new(buffer, &alphabet, &named_pat, &custom, false).matches(false); + let reverse = false; + let results = Model::new( + buffer, + &alphabet, + use_all_patterns, + &named_pat, + &custom, + reverse, + ) + .matches(false); assert_eq!(results.len(), 2); assert_eq!(results.get(0).unwrap().text, "http://foo.bar"); diff --git a/src/ui.rs b/src/ui.rs index 681621f..dc402a9 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -879,10 +879,19 @@ path: /usr/local/bin/cargo"; Barcelona https://en.wikipedia.org/wiki/Barcelona - "; + let use_all_patterns = true; let named_pat = vec![]; - let custom_regexes = vec![]; + let custom_patterns = vec![]; let alphabet = alphabets::Alphabet("abcd".to_string()); - let mut model = model::Model::new(content, &alphabet, &named_pat, &custom_regexes, false); + let reverse = false; + let mut model = model::Model::new( + content, + &alphabet, + use_all_patterns, + &named_pat, + &custom_patterns, + reverse, + ); let term_width: u16 = 80; let line_offsets = get_line_offsets(&model.lines, term_width); let rendering_colors = UiColors { @@ -944,11 +953,19 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; Barcelona https://en.wikipedia.org/wiki/Barcelona - "; + let use_all_patterns = true; let named_pat = vec![]; - let custom_regexes = vec![]; + let custom_patterns = vec![]; let alphabet = alphabets::Alphabet("abcd".to_string()); let reverse = true; - let mut model = model::Model::new(content, &alphabet, &named_pat, &custom_regexes, reverse); + let mut model = model::Model::new( + content, + &alphabet, + use_all_patterns, + &named_pat, + &custom_patterns, + reverse, + ); let unique_hint = false; let wrap_around = false; @@ -1071,11 +1088,11 @@ Barcelona https://en.wikipedia.org/wiki/Barcelona - "; } } -/// Holds color-related data, for clarity. -/// -/// - `focus_*` colors are used to render the currently focused matched text. -/// - `normal_*` colors are used to render other matched text. -/// - `hint_*` colors are used to render the hints. +// /// Holds color-related data, for clarity. +// /// +// /// - `focus_*` colors are used to render the currently focused matched text. +// /// - `normal_*` colors are used to render other matched text. +// /// - `hint_*` colors are used to render the hints. #[derive(Clap, Debug)] pub struct UiColors { /// Foreground color for base text.