From 1cba9ce5308562cfa404446e6f365fdee8439bf3 Mon Sep 17 00:00:00 2001 From: Ferran Basora Date: Thu, 30 Apr 2020 16:10:07 +0000 Subject: [PATCH] First termion attempt --- .rustfmt.toml | 1 + Cargo.lock | 169 +++----------------- Cargo.toml | 2 +- src/alphabets.rs | 6 +- src/colors.rs | 47 +++--- src/main.rs | 28 +++- src/state.rs | 71 ++------- src/swapper.rs | 53 ++----- src/view.rs | 396 +++++++++++++++++++++++++---------------------- 9 files changed, 305 insertions(+), 468 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index b196eaa..44f8e3a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1 +1,2 @@ tab_spaces = 2 +max_width = 120 diff --git a/Cargo.lock b/Cargo.lock index 89ad25a..306abd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,34 +25,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "autocfg" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "clap" version = "2.33.0" @@ -67,25 +44,6 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "gag" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -102,68 +60,23 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "num-traits" -version = "0.1.43" +name = "numtoa" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-traits" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "1.3.1" @@ -180,48 +93,22 @@ name = "regex-syntax" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustbox" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gag 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "termbox-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "tempfile" -version = "3.1.0" +name = "termion" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "termbox-sys" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "textwrap" version = "0.11.0" @@ -244,7 +131,7 @@ version = "0.3.1" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustbox 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -257,11 +144,6 @@ name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "wasi" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "winapi" version = "0.3.8" @@ -285,37 +167,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" -"checksum bitflags 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a41f80ec2e140d19e789764fdf22d0f2da98fe7e55d26f99db59cb3d2605d327" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum gag 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8cc0b9f53275dc5fada808f1d2f82e3688a6c14d735633d1590b7be8eb2307b5" -"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rustbox 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8efd06a5d2faf705b3257e8ce1a039b82d777f84ea203e93dfbcc9c86cf750b1" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum termbox-sys 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "afbf597ba2137c0f99b2675988701dc1cd70c2aaa213002a133b08e08fbf42ee" +"checksum termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index e4646f1..e8f6a84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["rust", "tmux", "tmux-plugin", "vimium", "vimperator"] license = "MIT" [dependencies] -rustbox = "0.11.0" +termion = "1.5" regex = "1.3.1" clap = "2.33.0" diff --git a/src/alphabets.rs b/src/alphabets.rs index 5709e61..25c1040 100644 --- a/src/alphabets.rs +++ b/src/alphabets.rs @@ -58,11 +58,7 @@ impl<'a> Alphabet<'a> { expanded.splice(0..0, sub_expansion); } - expansion = expansion - .iter() - .take(matches - expanded.len()) - .cloned() - .collect(); + expansion = expansion.iter().take(matches - expanded.len()).cloned().collect(); expansion.append(&mut expanded); expansion } diff --git a/src/colors.rs b/src/colors.rs index 3ecffc1..be5f92d 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,26 +1,18 @@ -use rustbox::Color; -use std::collections::HashMap; +use termion::color; -const COLORS: [(&'static str, Color); 9] = [ - ("black", Color::Black), - ("red", Color::Red), - ("green", Color::Green), - ("yellow", Color::Yellow), - ("blue", Color::Blue), - ("magenta", Color::Magenta), - ("cyan", Color::Cyan), - ("white", Color::White), - ("default", Color::Default), -]; - -pub fn get_color(color_name: &str) -> Color { - 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[&color_name] +pub fn get_color(color_name: &str) -> Box<&dyn color::Color> { + match color_name { + "black" => Box::new(&color::Black), + "red" => Box::new(&color::Red), + "green" => Box::new(&color::Green), + "yellow" => Box::new(&color::Yellow), + "blue" => Box::new(&color::Blue), + "magenta" => Box::new(&color::Magenta), + "cyan" => Box::new(&color::Cyan), + "white" => Box::new(&color::White), + "default" => Box::new(&color::Reset), + _ => panic!("Unknown color: {}", color_name), + } } #[cfg(test)] @@ -29,6 +21,15 @@ mod tests { #[test] fn match_color() { - assert_eq!(get_color("green"), Color::Green); + let text1 = println!("{}{}", color::Fg(*get_color("green")), "foo"); + let text2 = println!("{}{}", color::Fg(color::Green), "foo"); + + assert_eq!(text1, text2); + } + + #[test] + #[should_panic] + fn no_match_color() { + println!("{}{}", color::Fg(*get_color("wat")), "foo"); } } diff --git a/src/main.rs b/src/main.rs index eecc4fa..0b37a03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ extern crate clap; -extern crate rustbox; +extern crate termion; mod alphabets; mod colors; @@ -8,6 +8,7 @@ mod view; use self::clap::{App, Arg}; use clap::crate_version; +use std::io::prelude::*; use std::io::{self, Read}; fn app_args<'a>() -> clap::ArgMatches<'a> { @@ -103,6 +104,13 @@ fn app_args<'a>() -> clap::ArgMatches<'a> { .long("contrast") .short("c"), ) + .arg( + Arg::with_name("target") + .help("Stores the hint in the specified path") + .long("target") + .default_value("") + .short("t"), + ) .get_matches() } @@ -111,6 +119,7 @@ fn main() { let format = args.value_of("format").unwrap(); let alphabet = args.value_of("alphabet").unwrap(); let position = args.value_of("position").unwrap(); + let target = args.value_of("target").unwrap(); let multi = args.is_present("multi"); let reverse = args.is_present("reverse"); let unique = args.is_present("unique"); @@ -125,10 +134,8 @@ fn main() { 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_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_background_color = - colors::get_color(args.value_of("select_background_color").unwrap()); + let select_foreground_color = colors::get_color(args.value_of("select_foreground_color").unwrap()); + let select_background_color = colors::get_color(args.value_of("select_background_color").unwrap()); let stdin = io::stdin(); let mut handle = stdin.lock(); @@ -162,16 +169,21 @@ fn main() { if !selected.is_empty() { for (text, upcase) in selected.iter() { let mut output = format.to_string(); + let break_line = if multi { "\n" } else { "" }; let upcase_value = if *upcase { "true" } else { "false" }; output = str::replace(&output, "%U", upcase_value); output = str::replace(&output, "%H", text.as_str()); - if multi { - println!("{}", output); - } else { + let output = format!("{}{}", output, break_line); + + if target.is_empty() { print!("{}", output); + } else { + let mut file = std::fs::File::create(target).expect("Unable to open the target file"); + + file.write(output.as_bytes()).unwrap(); } } } else { diff --git a/src/state.rs b/src/state.rs index f351c6e..2c57e10 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,23 +2,16 @@ use regex::Regex; use std::collections::HashMap; use std::fmt; -const EXCLUDE_PATTERNS: [(&'static str, &'static str); 1] = - [("bash", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m")]; +const EXCLUDE_PATTERNS: [(&'static str, &'static str); 1] = [("bash", r"[[:cntrl:]]\[([0-9]{1,2};)?([0-9]{1,2})?m")]; const PATTERNS: [(&'static str, &'static str); 13] = [ ("markdown_url", r"\[[^]]*\]\(([^)]+)\)"), - ( - "url", - r"((https?://|git@|git://|ssh://|ftp://|file:///)[^ ]+)", - ), + ("url", r"((https?://|git@|git://|ssh://|ftp://|file:///)[^ ]+)"), ("diff_a", r"--- a/([^ ]+)"), ("diff_b", r"\+\+\+ b/([^ ]+)"), ("path", r"(([.\w\-@]+)?(/[.\w\-@]+)+)"), ("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}"), ("ipfs", r"Qm[0-9a-zA-Z]{44}"), ("sha", r"[0-9a-f]{7,40}"), ("ip", r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"), @@ -104,9 +97,7 @@ impl<'a> State<'a> { None => None, }) .collect::>(); - 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 { let (name, pattern, matching) = first_match; @@ -211,9 +202,7 @@ mod tests { #[test] fn match_bash() { - let lines = split( - "path: /var/log/nginx.log\npath: test/log/nginx-2.log:32folder/.nginx@4df2.log", - ); + let lines = split("path: /var/log/nginx.log\npath: test/log/nginx-2.log:32folder/.nginx@4df2.log"); let custom = [].to_vec(); let results = State::new(&lines, "abcd", &custom).matches(false, false); @@ -225,18 +214,13 @@ mod tests { #[test] fn match_paths() { - let lines = split( - "Lorem /tmp/foo/bar_lol, lorem\n Lorem /var/log/boot-strap.log lorem ../log/kern.log lorem", - ); + let lines = split("Lorem /tmp/foo/bar_lol, lorem\n Lorem /var/log/boot-strap.log lorem ../log/kern.log lorem"); let custom = [].to_vec(); let results = State::new(&lines, "abcd", &custom).matches(false, false); assert_eq!(results.len(), 3); assert_eq!(results.get(0).unwrap().text.clone(), "/tmp/foo/bar_lol"); - assert_eq!( - results.get(1).unwrap().text.clone(), - "/var/log/boot-strap.log" - ); + assert_eq!(results.get(1).unwrap().text.clone(), "/var/log/boot-strap.log"); assert_eq!(results.get(2).unwrap().text.clone(), "../log/kern.log"); } @@ -290,30 +274,20 @@ mod tests { "2001:67c:670:202:7ba8:5e41:1591:d723" ); assert_eq!(results.get(2).unwrap().text.clone(), "fe80::2:1"); - assert_eq!( - results.get(3).unwrap().text.clone(), - "fe80:22:312:fe::1%eth0" - ); + assert_eq!(results.get(3).unwrap().text.clone(), "fe80:22:312:fe::1%eth0"); } #[test] fn match_markdown_urls() { - let lines = - split("Lorem ipsum [link](https://github.io?foo=bar) ![](http://cdn.com/img.jpg) lorem"); + let lines = split("Lorem ipsum [link](https://github.io?foo=bar) ![](http://cdn.com/img.jpg) lorem"); let custom = [].to_vec(); let results = State::new(&lines, "abcd", &custom).matches(false, false); assert_eq!(results.len(), 2); assert_eq!(results.get(0).unwrap().pattern.clone(), "markdown_url"); - assert_eq!( - results.get(0).unwrap().text.clone(), - "https://github.io?foo=bar" - ); + assert_eq!(results.get(0).unwrap().text.clone(), "https://github.io?foo=bar"); assert_eq!(results.get(1).unwrap().pattern.clone(), "markdown_url"); - assert_eq!( - results.get(1).unwrap().text.clone(), - "http://cdn.com/img.jpg" - ); + assert_eq!(results.get(1).unwrap().text.clone(), "http://cdn.com/img.jpg"); } #[test] @@ -323,17 +297,11 @@ mod tests { let results = State::new(&lines, "abcd", &custom).matches(false, false); assert_eq!(results.len(), 4); - assert_eq!( - results.get(0).unwrap().text.clone(), - "https://www.rust-lang.org/tools" - ); + assert_eq!(results.get(0).unwrap().text.clone(), "https://www.rust-lang.org/tools"); assert_eq!(results.get(0).unwrap().pattern.clone(), "url"); assert_eq!(results.get(1).unwrap().text.clone(), "https://crates.io"); assert_eq!(results.get(1).unwrap().pattern.clone(), "url"); - assert_eq!( - results.get(2).unwrap().text.clone(), - "https://github.io?foo=bar" - ); + assert_eq!(results.get(2).unwrap().text.clone(), "https://github.io?foo=bar"); assert_eq!(results.get(2).unwrap().pattern.clone(), "url"); assert_eq!(results.get(3).unwrap().text.clone(), "ssh://github.io"); assert_eq!(results.get(3).unwrap().pattern.clone(), "url"); @@ -379,7 +347,8 @@ mod tests { #[test] 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 results = State::new(&lines, "abcd", &custom).matches(false, false); @@ -416,10 +385,7 @@ mod tests { assert_eq!(results.get(0).unwrap().text.clone(), "http://foo.bar"); assert_eq!(results.get(1).unwrap().text.clone(), "CUSTOM-52463"); assert_eq!(results.get(2).unwrap().text.clone(), "ISSUE-123"); - assert_eq!( - results.get(3).unwrap().text.clone(), - "/var/fd70b569/9999.log" - ); + assert_eq!(results.get(3).unwrap().text.clone(), "/var/fd70b569/9999.log"); assert_eq!(results.get(4).unwrap().text.clone(), "52463"); assert_eq!(results.get(5).unwrap().text.clone(), "973113"); assert_eq!( @@ -427,9 +393,6 @@ mod tests { "123e4567-e89b-12d3-a456-426655440000" ); assert_eq!(results.get(7).unwrap().text.clone(), "8888"); - assert_eq!( - results.get(8).unwrap().text.clone(), - "https://crates.io/23456/fd70b569" - ); + assert_eq!(results.get(8).unwrap().text.clone(), "https://crates.io/23456/fd70b569"); } } diff --git a/src/swapper.rs b/src/swapper.rs index 4bfa274..b1d3329 100644 --- a/src/swapper.rs +++ b/src/swapper.rs @@ -57,12 +57,7 @@ pub struct Swapper<'a> { } impl<'a> Swapper<'a> { - fn new( - executor: Box<&'a mut dyn Executor>, - dir: String, - command: String, - upcase_command: String, - ) -> Swapper { + fn new(executor: Box<&'a mut dyn Executor>, dir: String, command: String, upcase_command: String) -> Swapper { let since_the_epoch = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Time went backwards"); @@ -96,10 +91,7 @@ impl<'a> Swapper<'a> { .execute(active_command.iter().map(|arg| arg.to_string()).collect()); let lines: Vec<&str> = output.split('\n').collect(); - let chunks: Vec> = lines - .into_iter() - .map(|line| line.split(':').collect()) - .collect(); + let chunks: Vec> = lines.into_iter().map(|line| line.split(':').collect()).collect(); let active_pane = chunks .iter() @@ -161,10 +153,7 @@ impl<'a> Swapper<'a> { ]; if string_params.iter().any(|&x| x == name) { - return vec![ - format!("--{}", name), - format!("'{}'", value), - ]; + return vec![format!("--{}", name), format!("'{}'", value)]; } if name.starts_with("regexp") { @@ -181,15 +170,10 @@ impl<'a> Swapper<'a> { let active_pane_id = self.active_pane_id.as_mut().unwrap().clone(); let scroll_params = if self.active_pane_in_copy_mode.is_some() { - if let (Some(pane_height), Some(scroll_position)) = ( - self.active_pane_scroll_position, - self.active_pane_scroll_position, - ) { - format!( - " -S {} -E {}", - -scroll_position, - pane_height - scroll_position - 1 - ) + if let (Some(pane_height), Some(scroll_position)) = + (self.active_pane_scroll_position, self.active_pane_scroll_position) + { + format!(" -S {} -E {}", -scroll_position, pane_height - scroll_position - 1) } else { "".to_string() } @@ -199,12 +183,12 @@ impl<'a> Swapper<'a> { // NOTE: For debugging add echo $PWD && sleep 5 after tee let pane_command = format!( - "tmux capture-pane -t {} -p{} | {}/target/release/thumbs -f '%U:%H' {} | tee {}; tmux swap-pane -t {}; tmux wait-for -S {}", + "tmux capture-pane -t {} -p{} | {}/target/release/thumbs -f '%U:%H' -t {} {}; tmux swap-pane -t {}; tmux wait-for -S {}", active_pane_id, scroll_params, self.dir, - args.join(" "), TMP_FILE, + args.join(" "), active_pane_id, self.signal ); @@ -257,7 +241,7 @@ impl<'a> Swapper<'a> { } pub fn destroy_content(&mut self) { - let retrieve_command = vec!["rm", "/tmp/thumbs-last"]; + let retrieve_command = vec!["rm", TMP_FILE]; let params = retrieve_command.iter().map(|arg| arg.to_string()).collect(); self.executor.execute(params); @@ -314,15 +298,9 @@ mod tests { #[test] fn retrieve_active_pane() { - let last_command_outputs = - vec!["%97:100:24:1:active\n%106:100:24:1:nope\n%107:100:24:1:nope\n".to_string()]; + let last_command_outputs = vec!["%97:100:24:1:active\n%106:100:24:1:nope\n%107:100:24:1:nope\n".to_string()]; let mut executor = TestShell::new(last_command_outputs); - let mut swapper = Swapper::new( - Box::new(&mut executor), - "".to_string(), - "".to_string(), - "".to_string(), - ); + let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string()); swapper.capture_active_pane(); @@ -338,12 +316,7 @@ mod tests { "%106:100:24:1:nope\n%98:100:24:1:active\n%107:100:24:1:nope\n".to_string(), ]; let mut executor = TestShell::new(last_command_outputs); - let mut swapper = Swapper::new( - Box::new(&mut executor), - "".to_string(), - "".to_string(), - "".to_string(), - ); + let mut swapper = Swapper::new(Box::new(&mut executor), "".to_string(), "".to_string(), "".to_string()); swapper.capture_active_pane(); swapper.execute_thumbs(); diff --git a/src/view.rs b/src/view.rs index 9ff1902..a324fbc 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,23 +1,30 @@ use super::*; -use rustbox::Key; -use rustbox::{Color, OutputMode, RustBox}; use std::char; -use std::default::Default; +use std::io::{stdout, Read, Write}; +use termion::async_stdin; +use termion::event::Key; +use termion::input::TermRead; +use termion::raw::IntoRawMode; +use termion::{clear, color, cursor}; pub struct View<'a> { state: &'a mut state::State<'a>, skip: usize, multi: bool, - reverse: bool, - unique: bool, contrast: bool, position: &'a str, - select_foreground_color: Color, - select_background_color: Color, - foreground_color: Color, - background_color: Color, - hint_background_color: Color, - hint_foreground_color: Color, + matches: Vec>, + select_foreground_color: Box<&'a dyn color::Color>, + select_background_color: Box<&'a dyn color::Color>, + foreground_color: Box<&'a dyn color::Color>, + background_color: Box<&'a dyn color::Color>, + hint_background_color: Box<&'a dyn color::Color>, + hint_foreground_color: Box<&'a dyn color::Color>, +} + +enum CaptureEvent { + Exit, + Hint(Vec<(String, bool)>), } impl<'a> View<'a> { @@ -28,21 +35,23 @@ impl<'a> View<'a> { unique: bool, contrast: bool, position: &'a str, - select_foreground_color: Color, - select_background_color: Color, - foreground_color: Color, - background_color: Color, - hint_foreground_color: Color, - hint_background_color: Color, + select_foreground_color: Box<&'a dyn color::Color>, + select_background_color: Box<&'a dyn color::Color>, + foreground_color: Box<&'a dyn color::Color>, + background_color: Box<&'a dyn color::Color>, + hint_foreground_color: Box<&'a dyn color::Color>, + hint_background_color: Box<&'a dyn color::Color>, ) -> View<'a> { + let matches = state.matches(reverse, unique); + let skip = if reverse { matches.len() - 1 } else { 0 }; + View { state, - skip: 0, + skip, multi, - reverse, - unique, contrast, position, + matches, select_foreground_color, select_background_color, foreground_color, @@ -58,8 +67,8 @@ impl<'a> View<'a> { } } - pub fn next(&mut self, maximum: usize) { - if self.skip < maximum { + pub fn next(&mut self) { + if self.skip < self.matches.len() - 1 { self.skip += 1; } } @@ -72,168 +81,184 @@ impl<'a> View<'a> { } } - pub fn present(&mut self) -> Vec<(String, bool)> { - let mut rustbox = match RustBox::init(Default::default()) { - Result::Ok(v) => v, - Result::Err(e) => panic!("{}", e), - }; + fn render(&self, stdout: &mut dyn Write) -> () { + println!("{}{}", clear::All, cursor::Hide); + stdout.flush().unwrap(); - rustbox.set_output_mode(OutputMode::EightBit); + for (index, line) in self.state.lines.iter().enumerate() { + let clean = line.trim_end_matches(|c: char| c.is_whitespace()); - let mut typed_hint: String = "".to_owned(); - 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(); - let mut selected; - let mut chosen = vec![]; + if !clean.is_empty() { + let text = self.make_hint_text(line); - self.skip = if self.reverse { matches.len() - 1 } else { 0 }; - - loop { - rustbox.clear(); - rustbox.present(); - - for (index, line) in self.state.lines.iter().enumerate() { - let clean = line.trim_end_matches(|c: char| c.is_whitespace()); - - if !clean.is_empty() { - let text = self.make_hint_text(line); - - rustbox.print( - 0, - index, - rustbox::RB_NORMAL, - Color::White, - Color::Black, - &text, - ); - } - } - - selected = matches.get(self.skip); - - for mat in matches.iter() { - let selected_color = if selected == Some(mat) { - self.select_foreground_color - } else { - self.foreground_color - }; - let selected_background_color = if selected == Some(mat) { - self.select_background_color - } else { - self.background_color - }; - - // Find long utf sequences and extract it from mat.x - let line = &self.state.lines[mat.y as usize]; - let prefix = &line[0..mat.x as usize]; - let extra = prefix.len() - prefix.chars().count(); - let offset = (mat.x as usize) - extra; - let text = self.make_hint_text(mat.text); - - rustbox.print( - offset, - mat.y as usize, - rustbox::RB_NORMAL, - selected_color, - selected_background_color, - &text, - ); - - if let Some(ref hint) = mat.hint { - let extra_position = if self.position == "left" { - 0 - } else { - text.len() - mat.hint.clone().unwrap().len() - }; - - let text = self.make_hint_text(hint.as_str()); - - rustbox.print( - offset + extra_position, - mat.y as usize, - rustbox::RB_BOLD, - self.hint_foreground_color, - self.hint_background_color, - &text, - ); - } - } - - rustbox.present(); - - match rustbox.poll_event(false) { - Ok(rustbox::Event::KeyEvent(key)) => match key { - Key::Esc => { - if self.multi && !typed_hint.is_empty() { - typed_hint.clear(); - } else { - break; - } - } - Key::Enter => match matches.iter().enumerate().find(|&h| h.0 == self.skip) { - Some(hm) => { - chosen.push((hm.1.text.to_string(), false)); - - if !self.multi { - return chosen; - } - } - _ => 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) => { - if ch == ' ' && self.multi { - return chosen; - } - - 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) => { - chosen.push((mat.text.to_string(), key != lower_key)); - - if self.multi { - typed_hint.clear(); - } else { - return chosen; - } - } - None => { - if !self.multi && typed_hint.len() >= longest_hint.len() { - break; - } - } - } - } - _ => {} - }, - Err(e) => panic!("{}", e), - _ => {} + println!("{goto}{text}", goto = cursor::Goto(1, index as u16 + 1), text = &text); } } - chosen + let selected = self.matches.get(self.skip); + + for mat in self.matches.iter() { + let selected_color = if selected == Some(mat) { + &self.select_foreground_color + } else { + &self.foreground_color + }; + let selected_background_color = if selected == Some(mat) { + &self.select_background_color + } else { + &self.background_color + }; + + // Find long utf sequences and extract it from mat.x + let line = &self.state.lines[mat.y as usize]; + let prefix = &line[0..mat.x as usize]; + let extra = prefix.len() - prefix.chars().count(); + let offset = (mat.x as u16) - (extra as u16); + let text = self.make_hint_text(mat.text); + + println!( + "{goto}{background}{foregroud}{text}{resetf}{resetb}", + goto = cursor::Goto(offset + 1, mat.y as u16 + 1), + foregroud = color::Fg(**selected_color), + background = color::Bg(**selected_background_color), + resetf = color::Fg(color::Reset), + resetb = color::Bg(color::Reset), + text = &text + ); + + if let Some(ref hint) = mat.hint { + let extra_position = if self.position == "left" { + 0 + } else { + text.len() - mat.hint.clone().unwrap().len() + }; + + let text = self.make_hint_text(hint.as_str()); + + println!( + "{goto}{background}{foregroud}{text}{resetf}{resetb}", + goto = cursor::Goto(offset + extra_position as u16 + 1, mat.y as u16 + 1), + foregroud = color::Fg(*self.hint_foreground_color), + background = color::Bg(*self.hint_background_color), + resetf = color::Fg(color::Reset), + resetb = color::Bg(color::Reset), + text = &text + ); + } + } + + stdout.flush().unwrap(); + } + + fn listen(&mut self, stdin: &mut dyn Read, stdout: &mut dyn Write) -> CaptureEvent { + let mut chosen = vec![]; + let mut typed_hint: String = "".to_owned(); + let longest_hint = self + .matches + .iter() + .filter_map(|m| m.hint.clone()) + .max_by(|x, y| x.len().cmp(&y.len())) + .unwrap() + .clone(); + + self.render(stdout); + + loop { + match stdin.keys().next() { + Some(key) => { + match key { + Ok(key) => { + match key { + Key::Esc => { + if self.multi && !typed_hint.is_empty() { + typed_hint.clear(); + } else { + break; + } + } + Key::Insert => match self.matches.iter().enumerate().find(|&h| h.0 == self.skip) { + Some(hm) => { + chosen.push((hm.1.text.to_string(), false)); + + if !self.multi { + return CaptureEvent::Hint(chosen); + } + } + _ => panic!("Match not found?"), + }, + Key::Up => { + self.prev(); + } + Key::Down => { + self.next(); + } + Key::Left => { + self.prev(); + } + Key::Right => { + self.next(); + } + Key::Char(ch) => { + if ch == ' ' && self.multi { + return CaptureEvent::Hint(chosen); + } + + let key = ch.to_string(); + let lower_key = key.to_lowercase(); + + typed_hint.push_str(lower_key.as_str()); + + let selection = self.matches.iter().find(|mat| mat.hint == Some(typed_hint.clone())); + + match selection { + Some(mat) => { + chosen.push((mat.text.to_string(), key != lower_key)); + + if self.multi { + typed_hint.clear(); + } else { + return CaptureEvent::Hint(chosen); + } + } + None => { + if !self.multi && typed_hint.len() >= longest_hint.len() { + break; + } + } + } + } + _ => { + // Unknown key + } + } + } + Err(err) => panic!(err), + } + } + _ => { + // Nothing in the buffer. Wait for a bit... + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + self.render(stdout); + } + + CaptureEvent::Exit + } + + pub fn present(&mut self) -> Vec<(String, bool)> { + let mut stdin = async_stdin(); + let mut stdout = stdout().into_raw_mode().unwrap(); + + let hints = match self.listen(&mut stdin, &mut stdout) { + CaptureEvent::Exit => vec![], + CaptureEvent::Hint(chosen) => chosen, + }; + + write!(stdout, "{}", termion::cursor::Show).unwrap(); + + hints } } @@ -254,16 +279,15 @@ mod tests { state: &mut state, skip: 0, multi: false, - reverse: false, - unique: false, contrast: false, position: &"", - select_foreground_color: rustbox::Color::Default, - select_background_color: rustbox::Color::Default, - foreground_color: rustbox::Color::Default, - background_color: rustbox::Color::Default, - hint_background_color: rustbox::Color::Default, - hint_foreground_color: rustbox::Color::Default, + matches: vec![], + select_foreground_color: colors::get_color("default"), + select_background_color: colors::get_color("default"), + foreground_color: colors::get_color("default"), + background_color: colors::get_color("default"), + hint_background_color: colors::get_color("default"), + hint_foreground_color: colors::get_color("default"), }; let result = view.make_hint_text("a");