diff --git a/src/bridge.rs b/src/bridge.rs index 8546dd2..dd9be5c 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -429,13 +429,14 @@ fn main() -> Result<(), error::ParseError> { // Screen, preventing copyrat::run to execute. let initial_pane = active_pane; - // Create a new window without switching to it. - let temp_pane: tmux::Pane = tmux::create_new_window("[copyrat]")?; + // Create a new window without switching to it, with a `sh` command + // for faster startup. + let temp_pane: tmux::Pane = tmux::create_new_window("[copyrat]", "sh")?; // Swap the two panes, changing the active pane to be the temp_pane. // After swap, temp_pane has the same height than the initial_pane // had before being swapped. - tmux::swap_panes(&initial_pane, &temp_pane)?; + tmux::swap_panes(initial_pane, &temp_pane)?; // Running copyrat now will render in the newly created temp_pane // (locking stdin, writing to its stdout), but this is almost @@ -443,7 +444,7 @@ fn main() -> Result<(), error::ParseError> { let selections = copyrat::run(buffer, &opt.cli_options); // Swap back the two panes, making initial_pane the active one again. - tmux::swap_panes(&temp_pane, &initial_pane)?; + tmux::swap_panes(&temp_pane, initial_pane)?; tmux::kill_pane(&temp_pane)?; diff --git a/src/lib.rs b/src/lib.rs index be776fe..54096f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,9 +66,7 @@ pub struct CliOpt { // /// Which existing regexes to use. // #[clap(short = "x", long, arg_enum)] // regex_id: Vec, - - // TODO: choose if pre-baked regexes is a good idea - // TODO: check if compiled regexes are possible + // /// Additional regex patterns. #[clap(short = "X", long)] custom_regex: Vec, diff --git a/src/tmux.rs b/src/tmux.rs index 313124c..2c009f6 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -124,47 +124,6 @@ pub fn get_options(prefix: &str) -> Result, ParseError> Ok(args) } -// pub fn toto() { -// let options_command = vec!["tmux", "show", "-g"]; -// let params: Vec = options_command.iter().map(|arg| arg.to_string()).collect(); -// let options = self.executor.execute(params); -// let lines: Vec<&str> = options.split('\n').collect(); - -// let pattern = Regex::new(r#"@thumbs-([\w\-0-9]+) "?(\w+)"?"#).unwrap(); - -// let args = lines -// .iter() -// .flat_map(|line| { -// if let Some(captures) = pattern.captures(line) { -// let name = captures.get(1).unwrap().as_str(); -// let value = captures.get(2).unwrap().as_str(); - -// let boolean_params = vec!["reverse", "unique", "contrast"]; - -// if boolean_params.iter().any(|&x| x == name) { -// return vec![format!("--{}", name)]; -// } - -// let string_params = vec![ -// "position", -// "fg-color", -// "bg-color", -// "hint-bg-color", -// "hint-fg-color", -// "select-fg-color", -// "select-bg-color", -// ]; - -// if string_params.iter().any(|&x| x == name) { -// return vec![format!("--{}", name), format!("'{}'", value)]; -// } - -// if name.starts_with("regexp") { -// return vec!["--regexp".to_string(), format!("'{}'", value)]; -// } -// } -// };} - #[derive(Clap, Debug)] pub enum CaptureRegion { /// The entire history. @@ -198,6 +157,10 @@ impl FromStr for CaptureRegion { /// `CaptureRegion` specifies if the visible area is captured, or the entire /// history. /// +/// # TODO +/// +/// Capture with `capture-pane -J` joins wrapped lines. +/// /// # Note /// /// If the pane is in normal mode, capturing the visible area can be done @@ -234,16 +197,18 @@ pub fn capture_pane(pane: &Pane, region: &CaptureRegion) -> Result Result { +pub fn create_new_window(name: &str, command: &str) -> Result { let args = vec!["new-window", "-P", "-d", "-n", name, "-F", - "#{pane_id}:#{?pane_in_mode,true,false}:#{pane_height}:#{scroll_position}:#{?pane_active,true,false}"]; + "#{pane_id}:#{?pane_in_mode,true,false}:#{pane_height}:#{scroll_position}:#{?pane_active,true,false}", + command]; let output = process::execute("tmux", &args)?; diff --git a/src/view.rs b/src/view.rs index ce27a8a..bf5bb25 100644 --- a/src/view.rs +++ b/src/view.rs @@ -3,14 +3,9 @@ use super::{colors, state}; use crate::error::ParseError; use clap::Clap; use std::char; -use std::io::{stdout, Read, Write}; +use std::io; use std::str::FromStr; -use termion::async_stdin; -use termion::event::Key; -use termion::input::TermRead; -use termion::raw::IntoRawMode; -use termion::screen::AlternateScreen; -use termion::{color, cursor, style}; +use termion::{self, color, cursor, event, style}; pub struct View<'a> { state: &'a mut state::State<'a>, @@ -149,7 +144,7 @@ impl<'a> View<'a> { /// # Notes /// - All trailing whitespaces are trimmed, empty lines are skipped. /// - This writes directly on the writer, avoiding extra allocation. - fn render_lines(stdout: &mut dyn Write, lines: &Vec<&str>) -> () { + fn render_lines(stdout: &mut dyn io::Write, lines: &Vec<&str>) -> () { for (index, line) in lines.iter().enumerate() { let trimmed_line = line.trim_end(); @@ -172,7 +167,7 @@ impl<'a> View<'a> { /// # Note /// This writes directly on the writer, avoiding extra allocation. fn render_matched_text( - stdout: &mut dyn Write, + stdout: &mut dyn io::Write, text: &str, focused: bool, offset: (usize, usize), @@ -210,7 +205,7 @@ impl<'a> View<'a> { /// # Note /// This writes directly on the writer, avoiding extra allocation. fn render_matched_hint( - stdout: &mut dyn Write, + stdout: &mut dyn io::Write, hint_text: &str, offset: (usize, usize), colors: &ViewColors, @@ -299,7 +294,7 @@ impl<'a> View<'a> { /// # Note /// Multibyte characters are taken into account, so that the Match's `text` /// and `hint` are rendered in their proper position. - fn render(&self, stdout: &mut dyn Write) -> () { + fn render(&self, stdout: &mut dyn io::Write) -> () { write!(stdout, "{}", cursor::Hide).unwrap(); // 1. Trim all lines and render non-empty ones. @@ -359,7 +354,9 @@ impl<'a> View<'a> { /// # Panics /// This function panics if termion cannot read the entered keys on stdin. /// This function also panics if the user types Insert on a line without hints. - fn listen(&mut self, stdin: &mut dyn Read, stdout: &mut dyn Write) -> CaptureEvent { + fn listen(&mut self, reader: &mut dyn io::Read, writer: &mut dyn io::Write) -> CaptureEvent { + use termion::input::TermRead; // Trait for `reader.keys().next()`. + if self.matches.is_empty() { return CaptureEvent::Exit; } @@ -374,11 +371,11 @@ impl<'a> View<'a> { .unwrap() .clone(); - self.render(stdout); + self.render(writer); loop { // This is an option of a result of a key... Let's pop error cases first. - let next_key = stdin.keys().next(); + let next_key = reader.keys().next(); if next_key.is_none() { // Nothing in the buffer. Wait for a bit... @@ -394,7 +391,7 @@ impl<'a> View<'a> { match key_res.unwrap() { // Clears an ongoing multi-hint selection, or exit. - Key::Esc => { + event::Key::Esc => { if self.multi && !typed_hint.is_empty() { typed_hint.clear(); } else { @@ -405,7 +402,7 @@ impl<'a> View<'a> { // In multi-selection mode, this appends the selected hint to the // vector of selections. In normal mode, this returns with the hint // selected. - Key::Insert => match self.matches.get(self.focus_index) { + event::Key::Insert => match self.matches.get(self.focus_index) { Some(mat) => { chosen.push((mat.text.to_string(), false)); @@ -417,15 +414,15 @@ impl<'a> View<'a> { }, // Move focus to next/prev match. - Key::Up => self.prev(), - Key::Down => self.next(), - Key::Left => self.prev(), - Key::Right => self.next(), + event::Key::Up => self.prev(), + event::Key::Down => self.next(), + event::Key::Left => self.prev(), + event::Key::Right => self.next(), // Pressing space finalizes an ongoing multi-hint selection (without // selecting the focused match). Pressing other characters attempts at // finding a match with a corresponding hint. - Key::Char(ch) => { + event::Key::Char(ch) => { if ch == ' ' && self.multi { return CaptureEvent::Hint(chosen); } @@ -468,15 +465,23 @@ impl<'a> View<'a> { // Render on stdout if we did not exit earlier (move focus, // multi-selection). - self.render(stdout); + self.render(writer); } CaptureEvent::Exit } pub fn present(&mut self) -> Vec<(String, bool)> { - let mut stdin = async_stdin(); - let mut stdout = AlternateScreen::from(stdout().into_raw_mode().unwrap()); + use std::io::Write; + use termion::raw::IntoRawMode; + use termion::screen::AlternateScreen; + + let mut stdin = termion::async_stdin(); + let mut stdout = AlternateScreen::from( + io::stdout() + .into_raw_mode() + .expect("Cannot access alternate screen."), + ); let hints = match self.listen(&mut stdin, &mut stdout) { CaptureEvent::Exit => vec![],