Format rules with rustfmt

This commit is contained in:
Ferran Basora 2019-03-17 20:16:55 +00:00
parent c828ff39d7
commit 64f1473551
7 changed files with 279 additions and 171 deletions

1
.rustfmt.toml Normal file
View File

@ -0,0 +1 @@
tab_spaces = 2

View File

@ -9,6 +9,10 @@ rust:
- nightly - nightly
- stable - stable
before_script:
- rustup component add rustfmt
script: script:
- cargo fmt --all -- --check
- cargo build - cargo build
- cargo test - cargo test

View File

@ -26,14 +26,12 @@ const ALPHABETS: [(&'static str, &'static str); 22] = [
]; ];
pub struct Alphabet<'a> { pub struct Alphabet<'a> {
letters: &'a str letters: &'a str,
} }
impl<'a> Alphabet<'a> { impl<'a> Alphabet<'a> {
fn new(letters: &'a str) -> Alphabet { fn new(letters: &'a str) -> Alphabet {
Alphabet{ Alphabet { letters: letters }
letters: letters
}
} }
pub fn hints(&self, matches: usize) -> Vec<String> { pub fn hints(&self, matches: usize) -> Vec<String> {
@ -43,16 +41,28 @@ impl<'a> Alphabet<'a> {
let mut expanded: Vec<String> = Vec::new(); let mut expanded: Vec<String> = Vec::new();
loop { loop {
if expansion.len() + expanded.len() >= matches { break; } if expansion.len() + expanded.len() >= matches {
if expansion.len() == 0 { break; } break;
}
if expansion.len() == 0 {
break;
}
let prefix = expansion.pop().expect("Ouch!"); let prefix = expansion.pop().expect("Ouch!");
let sub_expansion: Vec<String> = letters.iter().take(matches - expansion.len() - expanded.len()).map(|s| prefix.clone() + s).collect(); let sub_expansion: Vec<String> = letters
.iter()
.take(matches - expansion.len() - expanded.len())
.map(|s| prefix.clone() + s)
.collect();
expanded.splice(0..0, sub_expansion); expanded.splice(0..0, sub_expansion);
} }
expansion = expansion.iter().take(matches - expanded.len()).map(|s| s.clone()).collect(); expansion = expansion
.iter()
.take(matches - expanded.len())
.map(|s| s.clone())
.collect();
expansion.append(&mut expanded); expansion.append(&mut expanded);
expansion expansion
} }
@ -61,7 +71,9 @@ impl<'a> Alphabet<'a> {
pub fn get_alphabet(alphabet_name: &str) -> Alphabet { pub fn get_alphabet(alphabet_name: &str) -> Alphabet {
let alphabets: HashMap<&str, &str> = ALPHABETS.iter().cloned().collect(); let alphabets: HashMap<&str, &str> = ALPHABETS.iter().cloned().collect();
alphabets.get(alphabet_name).expect(format!("Unknown alphabet: {}", alphabet_name).as_str()); // FIXME alphabets
.get(alphabet_name)
.expect(format!("Unknown alphabet: {}", alphabet_name).as_str()); // FIXME
Alphabet::new(alphabets[alphabet_name]) Alphabet::new(alphabets[alphabet_name])
} }
@ -71,28 +83,28 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn simple_matches () { fn simple_matches() {
let alphabet = Alphabet::new("abcd"); let alphabet = Alphabet::new("abcd");
let hints = alphabet.hints(3); let hints = alphabet.hints(3);
assert_eq!(hints, ["a", "b", "c"]); assert_eq!(hints, ["a", "b", "c"]);
} }
#[test] #[test]
fn composed_matches () { fn composed_matches() {
let alphabet = Alphabet::new("abcd"); let alphabet = Alphabet::new("abcd");
let hints = alphabet.hints(6); let hints = alphabet.hints(6);
assert_eq!(hints, ["a", "b", "c", "da", "db", "dc"]); assert_eq!(hints, ["a", "b", "c", "da", "db", "dc"]);
} }
#[test] #[test]
fn composed_matches_multiple () { fn composed_matches_multiple() {
let alphabet = Alphabet::new("abcd"); let alphabet = Alphabet::new("abcd");
let hints = alphabet.hints(8); let hints = alphabet.hints(8);
assert_eq!(hints, ["a", "b", "ca", "cb", "da", "db", "dc", "dd"]); assert_eq!(hints, ["a", "b", "ca", "cb", "da", "db", "dc", "dd"]);
} }
#[test] #[test]
fn composed_matches_max () { fn composed_matches_max() {
let alphabet = Alphabet::new("ab"); let alphabet = Alphabet::new("ab");
let hints = alphabet.hints(8); let hints = alphabet.hints(8);
assert_eq!(hints, ["aa", "ab", "ba", "bb"]); assert_eq!(hints, ["aa", "ab", "ba", "bb"]);

View File

@ -16,7 +16,9 @@ const COLORS: [(&'static str, Color); 9] = [
pub fn get_color(color_name: &str) -> Color { pub fn get_color(color_name: &str) -> Color {
let available_colors: HashMap<&str, Color> = COLORS.iter().cloned().collect(); let available_colors: HashMap<&str, Color> = COLORS.iter().cloned().collect();
available_colors.get(color_name).expect(format!("Unknown color: {}", color_name).as_str()); // FIXME available_colors
.get(color_name)
.expect(format!("Unknown color: {}", color_name).as_str()); // FIXME
available_colors[&color_name] available_colors[&color_name]
} }
@ -26,7 +28,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn match_color () { fn match_color() {
assert_eq!(get_color("green"), Color::Green); assert_eq!(get_color("green"), Color::Green);
} }
} }

View File

@ -1,14 +1,14 @@
extern crate rustbox;
extern crate clap; extern crate clap;
extern crate rustbox;
mod state;
mod alphabets; mod alphabets;
mod colors; mod colors;
mod state;
mod view; mod view;
use self::clap::{Arg, App}; use self::clap::{App, Arg};
use std::process::Command;
use clap::crate_version; use clap::crate_version;
use std::process::Command;
fn exec_command(command: String) -> std::process::Output { fn exec_command(command: String) -> std::process::Output {
let args: Vec<_> = command.split(" ").collect(); let args: Vec<_> = command.split(" ").collect();
@ -19,66 +19,92 @@ fn exec_command(command: String) -> std::process::Output {
.expect("Couldn't run it"); .expect("Couldn't run it");
} }
fn app_args<'a> () -> clap::ArgMatches<'a> { fn app_args<'a>() -> clap::ArgMatches<'a> {
return App::new("tmux-thumbs") return App::new("tmux-thumbs")
.version(crate_version!()) .version(crate_version!())
.about("A lightning fast version of tmux-fingers, copy/pasting tmux like vimium/vimperator") .about("A lightning fast version of tmux-fingers, copy/pasting tmux like vimium/vimperator")
.arg(Arg::with_name("alphabet") .arg(
.help("Sets the alphabet") Arg::with_name("alphabet")
.long("alphabet") .help("Sets the alphabet")
.short("a") .long("alphabet")
.default_value("qwerty")) .short("a")
.arg(Arg::with_name("foreground_color") .default_value("qwerty"),
.help("Sets the foregroud color for matches") )
.long("fg-color") .arg(
.default_value("green")) Arg::with_name("foreground_color")
.arg(Arg::with_name("background_color") .help("Sets the foregroud color for matches")
.help("Sets the background color for matches") .long("fg-color")
.long("bg-color") .default_value("green"),
.default_value("black")) )
.arg(Arg::with_name("hint_foreground_color") .arg(
.help("Sets the foregroud color for hints") Arg::with_name("background_color")
.long("hint-fg-color") .help("Sets the background color for matches")
.default_value("yellow")) .long("bg-color")
.arg(Arg::with_name("hint_background_color") .default_value("black"),
.help("Sets the background color for hints") )
.long("hint-bg-color") .arg(
.default_value("black")) Arg::with_name("hint_foreground_color")
.arg(Arg::with_name("select_foreground_color") .help("Sets the foregroud color for hints")
.help("Sets the foregroud color for selection") .long("hint-fg-color")
.long("select-fg-color") .default_value("yellow"),
.default_value("blue")) )
.arg(Arg::with_name("reverse") .arg(
.help("Reverse the order for assigned hints") Arg::with_name("hint_background_color")
.long("reverse") .help("Sets the background color for hints")
.short("r")) .long("hint-bg-color")
.arg(Arg::with_name("unique") .default_value("black"),
.help("Don't show duplicated hints for the same match") )
.long("unique") .arg(
.short("u")) Arg::with_name("select_foreground_color")
.arg(Arg::with_name("position") .help("Sets the foregroud color for selection")
.help("Hint position") .long("select-fg-color")
.long("position") .default_value("blue"),
.default_value("left") )
.short("p")) .arg(
.arg(Arg::with_name("tmux_pane") Arg::with_name("reverse")
.help("Get this tmux pane as reference pane") .help("Reverse the order for assigned hints")
.long("tmux-pane") .long("reverse")
.takes_value(true)) .short("r"),
.arg(Arg::with_name("command") )
.help("Pick command") .arg(
.long("command") Arg::with_name("unique")
.default_value("tmux set-buffer {}")) .help("Don't show duplicated hints for the same match")
.arg(Arg::with_name("upcase_command") .long("unique")
.help("Upcase command") .short("u"),
.long("upcase-command") )
.default_value("tmux paste-buffer")) .arg(
.arg(Arg::with_name("regexp") Arg::with_name("position")
.help("Use this regexp as extra pattern to match") .help("Hint position")
.long("regexp") .long("position")
.short("x") .default_value("left")
.takes_value(true) .short("p"),
.multiple(true)) )
.arg(
Arg::with_name("tmux_pane")
.help("Get this tmux pane as reference pane")
.long("tmux-pane")
.takes_value(true),
)
.arg(
Arg::with_name("command")
.help("Pick command")
.long("command")
.default_value("tmux set-buffer {}"),
)
.arg(
Arg::with_name("upcase_command")
.help("Upcase command")
.long("upcase-command")
.default_value("tmux paste-buffer"),
)
.arg(
Arg::with_name("regexp")
.help("Use this regexp as extra pattern to match")
.long("regexp")
.short("x")
.takes_value(true)
.multiple(true),
)
.get_matches(); .get_matches();
} }
@ -98,7 +124,8 @@ fn main() {
let background_color = colors::get_color(args.value_of("background_color").unwrap()); let background_color = colors::get_color(args.value_of("background_color").unwrap());
let hint_foreground_color = colors::get_color(args.value_of("hint_foreground_color").unwrap()); let hint_foreground_color = colors::get_color(args.value_of("hint_foreground_color").unwrap());
let hint_background_color = colors::get_color(args.value_of("hint_background_color").unwrap()); let hint_background_color = colors::get_color(args.value_of("hint_background_color").unwrap());
let select_foreground_color = colors::get_color(args.value_of("select_foreground_color").unwrap()); let select_foreground_color =
colors::get_color(args.value_of("select_foreground_color").unwrap());
let command = args.value_of("command").unwrap(); let command = args.value_of("command").unwrap();
let upcase_command = args.value_of("upcase_command").unwrap(); let upcase_command = args.value_of("upcase_command").unwrap();
@ -124,7 +151,7 @@ fn main() {
foreground_color, foreground_color,
background_color, background_color,
hint_foreground_color, hint_foreground_color,
hint_background_color hint_background_color,
); );
viewbox.present() viewbox.present()
@ -134,7 +161,6 @@ fn main() {
exec_command(format!("tmux swap-pane -t {}", pane)); exec_command(format!("tmux swap-pane -t {}", pane));
}; };
if let Some((text, paste)) = selected { if let Some((text, paste)) = selected {
exec_command(str::replace(command, "{}", text.as_str())); exec_command(str::replace(command, "{}", text.as_str()));
@ -142,5 +168,4 @@ fn main() {
exec_command(upcase_command.to_string()); exec_command(upcase_command.to_string());
} }
} }
} }

View File

@ -1,18 +1,23 @@
use std::collections::HashMap;
use regex::Regex; use regex::Regex;
use std::collections::HashMap;
use std::fmt; use std::fmt;
const EXCLUDE_PATTERNS: [(&'static str, &'static str); 1] = [ const EXCLUDE_PATTERNS: [(&'static str, &'static str); 1] =
("bash", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m"), [("bash", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m")];
];
const PATTERNS: [(&'static str, &'static str); 10] = [ const PATTERNS: [(&'static str, &'static str); 10] = [
("url", r"((https?://|git@|git://|ssh://|ftp://|file:///)[\w?=%/_.:,;~@!#$&()*+-]*)"), (
"url",
r"((https?://|git@|git://|ssh://|ftp://|file:///)[\w?=%/_.:,;~@!#$&()*+-]*)",
),
("diff_a", r"--- a/([^ ]+)"), ("diff_a", r"--- a/([^ ]+)"),
("diff_b", r"\+\+\+ b/([^ ]+)"), ("diff_b", r"\+\+\+ b/([^ ]+)"),
("path", r"[^ ]+/[^ [[:cntrl:]]]+"), ("path", r"[^ ]+/[^ [[:cntrl:]]]+"),
("color", r"#[0-9a-fA-F]{6}"), ("color", r"#[0-9a-fA-F]{6}"),
("uid", r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"), (
"uid",
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}",
),
("sha", r"[0-9a-f]{7,40}"), ("sha", r"[0-9a-f]{7,40}"),
("ip", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"), ("ip", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"),
("address", r"0x[0-9a-fA-F]+"), ("address", r"0x[0-9a-fA-F]+"),
@ -24,12 +29,19 @@ pub struct Match<'a> {
pub x: i32, pub x: i32,
pub y: i32, pub y: i32,
pub text: &'a str, pub text: &'a str,
pub hint: Option<String> pub hint: Option<String>,
} }
impl<'a> fmt::Debug for Match<'a> { impl<'a> fmt::Debug for Match<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Match {{ x: {}, y: {}, text: {}, hint: <{}> }}", self.x, self.y, self.text, self.hint.clone().unwrap_or("<undefined>".to_string())) write!(
f,
"Match {{ x: {}, y: {}, text: {}, hint: <{}> }}",
self.x,
self.y,
self.text,
self.hint.clone().unwrap_or("<undefined>".to_string())
)
} }
} }
@ -47,27 +59,31 @@ pub struct State<'a> {
impl<'a> State<'a> { impl<'a> State<'a> {
pub fn new(lines: &'a Vec<&'a str>, alphabet: &'a str, regexp: &'a Vec<&'a str>) -> State<'a> { pub fn new(lines: &'a Vec<&'a str>, alphabet: &'a str, regexp: &'a Vec<&'a str>) -> State<'a> {
State{ State {
lines: lines, lines: lines,
alphabet: alphabet, alphabet: alphabet,
regexp: regexp regexp: regexp,
} }
} }
pub fn matches(&self, reverse: bool, unique: bool) -> Vec<Match<'a>> { pub fn matches(&self, reverse: bool, unique: bool) -> Vec<Match<'a>> {
let mut matches = Vec::new(); let mut matches = Vec::new();
let exclude_patterns = EXCLUDE_PATTERNS.iter().map(|tuple| let exclude_patterns = EXCLUDE_PATTERNS
(tuple.0, Regex::new(tuple.1).unwrap()) .iter()
).collect::<Vec<_>>(); .map(|tuple| (tuple.0, Regex::new(tuple.1).unwrap()))
.collect::<Vec<_>>();
let custom_patterns = self.regexp.iter().map(|regexp| let custom_patterns = self
("custom", Regex::new(regexp).expect("Invalid custom regexp")) .regexp
).collect::<Vec<_>>(); .iter()
.map(|regexp| ("custom", Regex::new(regexp).expect("Invalid custom regexp")))
.collect::<Vec<_>>();
let patterns = PATTERNS.iter().map(|tuple| let patterns = PATTERNS
(tuple.0, Regex::new(tuple.1).unwrap()) .iter()
).collect::<Vec<_>>(); .map(|tuple| (tuple.0, Regex::new(tuple.1).unwrap()))
.collect::<Vec<_>>();
let all_patterns = [exclude_patterns, custom_patterns, patterns].concat(); let all_patterns = [exclude_patterns, custom_patterns, patterns].concat();
@ -76,13 +92,16 @@ impl<'a> State<'a> {
let mut offset: i32 = 0; let mut offset: i32 = 0;
loop { loop {
let submatches = all_patterns.iter().filter_map(|tuple| let submatches = all_patterns
match tuple.1.find_iter(chunk).nth(0) { .iter()
.filter_map(|tuple| match tuple.1.find_iter(chunk).nth(0) {
Some(m) => Some((tuple.0, tuple.1.clone(), m)), Some(m) => Some((tuple.0, tuple.1.clone(), m)),
None => None None => None,
} })
).collect::<Vec<_>>(); .collect::<Vec<_>>();
let first_match_option = submatches.iter().min_by(|x, y| x.2.start().cmp(&y.2.start())); let first_match_option = submatches
.iter()
.min_by(|x, y| x.2.start().cmp(&y.2.start()));
if let Some(first_match) = first_match_option { if let Some(first_match) = first_match_option {
let (name, pattern, matching) = first_match; let (name, pattern, matching) = first_match;
@ -97,17 +116,16 @@ impl<'a> State<'a> {
// Never hint or broke bash color sequences // Never hint or broke bash color sequences
if *name != "bash" { if *name != "bash" {
matches.push(Match{ matches.push(Match {
x: offset + matching.start() as i32 + substart as i32, x: offset + matching.start() as i32 + substart as i32,
y: index as i32, y: index as i32,
text: subtext, text: subtext,
hint: None hint: None,
}); });
} }
chunk = chunk.get(matching.end()..).expect("Unknown chunk"); chunk = chunk.get(matching.end()..).expect("Unknown chunk");
offset = offset + matching.end() as i32; offset = offset + matching.end() as i32;
} else { } else {
panic!("No matching?"); panic!("No matching?");
} }
@ -164,7 +182,7 @@ mod tests {
} }
#[test] #[test]
fn match_reverse () { fn match_reverse() {
let lines = split("lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem"); let lines = split("lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -175,7 +193,7 @@ mod tests {
} }
#[test] #[test]
fn match_unique () { fn match_unique() {
let lines = split("lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem"); let lines = split("lorem 127.0.0.1 lorem 255.255.255.255 lorem 127.0.0.1 lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, true); let results = State::new(&lines, "abcd", &custom).matches(false, true);
@ -186,7 +204,7 @@ mod tests {
} }
#[test] #[test]
fn match_bash () { fn match_bash() {
let lines = split("path: /var/log/nginx.log\npath: test/log/nginx.log"); let lines = split("path: /var/log/nginx.log\npath: test/log/nginx.log");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -195,8 +213,9 @@ mod tests {
} }
#[test] #[test]
fn match_paths () { fn match_paths() {
let lines = split("Lorem /tmp/foo/bar lorem\n Lorem /var/log/bootstrap.log lorem ../log/kern.log lorem"); let lines =
split("Lorem /tmp/foo/bar lorem\n Lorem /var/log/bootstrap.log lorem ../log/kern.log lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -204,7 +223,7 @@ mod tests {
} }
#[test] #[test]
fn match_uids () { fn match_uids() {
let lines = split("Lorem ipsum 123e4567-e89b-12d3-a456-426655440000 lorem\n Lorem lorem lorem"); let lines = split("Lorem ipsum 123e4567-e89b-12d3-a456-426655440000 lorem\n Lorem lorem lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -213,7 +232,7 @@ mod tests {
} }
#[test] #[test]
fn match_shas () { fn match_shas() {
let lines = split("Lorem fd70b5695 5246ddf f924213 lorem\n Lorem 973113963b491874ab2e372ee60d4b4cb75f717c lorem"); let lines = split("Lorem fd70b5695 5246ddf f924213 lorem\n Lorem 973113963b491874ab2e372ee60d4b4cb75f717c lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -222,7 +241,7 @@ mod tests {
} }
#[test] #[test]
fn match_ips () { fn match_ips() {
let lines = split("Lorem ipsum 127.0.0.1 lorem\n Lorem 255.255.10.255 lorem 127.0.0.1 lorem"); let lines = split("Lorem ipsum 127.0.0.1 lorem\n Lorem 255.255.10.255 lorem 127.0.0.1 lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -231,7 +250,7 @@ mod tests {
} }
#[test] #[test]
fn match_urls () { fn match_urls() {
let lines = split("Lorem ipsum https://www.rust-lang.org/tools lorem\n Lorem https://crates.io lorem https://github.io lorem ssh://github.io"); let lines = split("Lorem ipsum https://www.rust-lang.org/tools lorem\n Lorem https://crates.io lorem https://github.io lorem ssh://github.io");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -240,7 +259,7 @@ mod tests {
} }
#[test] #[test]
fn match_addresses () { fn match_addresses() {
let lines = split("Lorem 0xfd70b5695 0x5246ddf lorem\n Lorem 0x973113 lorem"); let lines = split("Lorem 0xfd70b5695 0x5246ddf lorem\n Lorem 0x973113 lorem");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -249,7 +268,7 @@ mod tests {
} }
#[test] #[test]
fn match_hex_colors () { fn match_hex_colors() {
let lines = split("Lorem #fd7b56 lorem #FF00FF\n Lorem #00fF05 lorem #abcd00 lorem #afRR00"); let lines = split("Lorem #fd7b56 lorem #FF00FF\n Lorem #00fF05 lorem #abcd00 lorem #afRR00");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -258,7 +277,7 @@ mod tests {
} }
#[test] #[test]
fn match_process_port () { fn match_process_port() {
let lines = split("Lorem 5695 52463 lorem\n Lorem 973113 lorem 99999 lorem 8888 lorem\n 23456 lorem 5432 lorem 23444"); let lines = split("Lorem 5695 52463 lorem\n Lorem 973113 lorem 99999 lorem 8888 lorem\n 23456 lorem 5432 lorem 23444");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -267,7 +286,7 @@ mod tests {
} }
#[test] #[test]
fn match_diff_a () { fn match_diff_a() {
let lines = split("Lorem lorem\n--- a/src/main.rs"); let lines = split("Lorem lorem\n--- a/src/main.rs");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -277,7 +296,7 @@ mod tests {
} }
#[test] #[test]
fn match_diff_b () { fn match_diff_b() {
let lines = split("Lorem lorem\n+++ b/src/main.rs"); let lines = split("Lorem lorem\n+++ b/src/main.rs");
let custom = [].to_vec(); let custom = [].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);
@ -287,7 +306,7 @@ mod tests {
} }
#[test] #[test]
fn priority () { fn priority() {
let lines = split("Lorem 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 lines = split("Lorem 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 custom = ["CUSTOM-[0-9]{4,}", "ISSUE-[0-9]{3}"].to_vec(); let custom = ["CUSTOM-[0-9]{4,}", "ISSUE-[0-9]{3}"].to_vec();
let results = State::new(&lines, "abcd", &custom).matches(false, false); let results = State::new(&lines, "abcd", &custom).matches(false, false);

View File

@ -1,8 +1,8 @@
use super::*;
use rustbox::Key;
use rustbox::{Color, OutputMode, RustBox};
use std::char; use std::char;
use std::default::Default; use std::default::Default;
use rustbox::{Color, RustBox, OutputMode};
use rustbox::Key;
use super::*;
pub struct View<'a> { pub struct View<'a> {
state: &'a mut state::State<'a>, state: &'a mut state::State<'a>,
@ -14,12 +14,22 @@ pub struct View<'a> {
foreground_color: Color, foreground_color: Color,
background_color: Color, background_color: Color,
hint_background_color: Color, hint_background_color: Color,
hint_foreground_color: Color hint_foreground_color: Color,
} }
impl<'a> View<'a> { impl<'a> View<'a> {
pub fn new(state: &'a mut state::State<'a>, reverse: bool, unique: bool, position: &'a str, select_foreground_color: Color, foreground_color: Color, background_color: Color, hint_foreground_color: Color, hint_background_color: Color) -> View<'a> { pub fn new(
View{ state: &'a mut state::State<'a>,
reverse: bool,
unique: bool,
position: &'a str,
select_foreground_color: Color,
foreground_color: Color,
background_color: Color,
hint_foreground_color: Color,
hint_background_color: Color,
) -> View<'a> {
View {
state: state, state: state,
skip: 0, skip: 0,
reverse: reverse, reverse: reverse,
@ -29,7 +39,7 @@ impl<'a> View<'a> {
foreground_color: foreground_color, foreground_color: foreground_color,
background_color: background_color, background_color: background_color,
hint_foreground_color: hint_foreground_color, hint_foreground_color: hint_foreground_color,
hint_background_color: hint_background_color hint_background_color: hint_background_color,
} }
} }
@ -55,7 +65,12 @@ impl<'a> View<'a> {
let mut typed_hint: String = "".to_owned(); let mut typed_hint: String = "".to_owned();
let matches = self.state.matches(self.reverse, self.unique); let matches = self.state.matches(self.reverse, self.unique);
let longest_hint = matches.iter().filter_map(|m| m.hint.clone()).max_by(|x, y| x.len().cmp(&y.len())).unwrap().clone(); let longest_hint = matches
.iter()
.filter_map(|m| m.hint.clone())
.max_by(|x, y| x.len().cmp(&y.len()))
.unwrap()
.clone();
let mut selected; let mut selected;
self.skip = if self.reverse { matches.len() - 1 } else { 0 }; self.skip = if self.reverse { matches.len() - 1 } else { 0 };
@ -68,7 +83,14 @@ impl<'a> View<'a> {
let clean = line.trim_end_matches(|c: char| c.is_whitespace()); let clean = line.trim_end_matches(|c: char| c.is_whitespace());
if clean.len() > 0 { if clean.len() > 0 {
rustbox.print(0, index, rustbox::RB_NORMAL, Color::White, Color::Black, line); rustbox.print(
0,
index,
rustbox::RB_NORMAL,
Color::White,
Color::Black,
line,
);
} }
} }
@ -87,55 +109,78 @@ impl<'a> View<'a> {
let extra = prefix.len() - prefix.chars().count(); let extra = prefix.len() - prefix.chars().count();
let offset = (mat.x as usize) - extra; let offset = (mat.x as usize) - extra;
rustbox.print(offset, mat.y as usize, rustbox::RB_NORMAL, selected_color, self.background_color, mat.text); rustbox.print(
offset,
mat.y as usize,
rustbox::RB_NORMAL,
selected_color,
self.background_color,
mat.text,
);
if let Some(ref hint) = mat.hint { if let Some(ref hint) = mat.hint {
let extra_position = if self.position == "left" { 0 } else { mat.text.len() - mat.hint.clone().unwrap().len() }; let extra_position = if self.position == "left" {
0
} else {
mat.text.len() - mat.hint.clone().unwrap().len()
};
rustbox.print(offset + extra_position, mat.y as usize, rustbox::RB_BOLD, self.hint_foreground_color, self.hint_background_color, hint.as_str()); rustbox.print(
offset + extra_position,
mat.y as usize,
rustbox::RB_BOLD,
self.hint_foreground_color,
self.hint_background_color,
hint.as_str(),
);
} }
} }
rustbox.present(); rustbox.present();
match rustbox.poll_event(false) { match rustbox.poll_event(false) {
Ok(rustbox::Event::KeyEvent(key)) => { Ok(rustbox::Event::KeyEvent(key)) => match key {
match key { Key::Esc => {
Key::Esc => { break; } break;
Key::Enter => {
match matches.iter().enumerate().find(|&h| h.0 == self.skip) {
Some(hm) => {
return Some((hm.1.text.to_string(), false))
}
_ => panic!("Match not found?"),
}
}
Key::Up => { self.prev(); }
Key::Down => { self.next(matches.len() - 1); }
Key::Left => { self.prev(); }
Key::Right => { self.next(matches.len() - 1); }
Key::Char(ch) => {
let key = ch.to_string();
let lower_key = key.to_lowercase();
typed_hint.push_str(lower_key.as_str());
match matches.iter().find(|mat| mat.hint == Some(typed_hint.clone())) {
Some(mat) => {
return Some((mat.text.to_string(), key != lower_key))
},
None => {
if typed_hint.len() >= longest_hint.len() {
break;
}
}
}
}
_ => {}
} }
} Key::Enter => match matches.iter().enumerate().find(|&h| h.0 == self.skip) {
Some(hm) => return Some((hm.1.text.to_string(), false)),
_ => panic!("Match not found?"),
},
Key::Up => {
self.prev();
}
Key::Down => {
self.next(matches.len() - 1);
}
Key::Left => {
self.prev();
}
Key::Right => {
self.next(matches.len() - 1);
}
Key::Char(ch) => {
let key = ch.to_string();
let lower_key = key.to_lowercase();
typed_hint.push_str(lower_key.as_str());
match matches
.iter()
.find(|mat| mat.hint == Some(typed_hint.clone()))
{
Some(mat) => return Some((mat.text.to_string(), key != lower_key)),
None => {
if typed_hint.len() >= longest_hint.len() {
break;
}
}
}
}
_ => {}
},
Err(e) => panic!("{}", e), Err(e) => panic!("{}", e),
_ => { } _ => {}
} }
} }