diff --git a/src/bin/tmux_copyrat.rs b/src/bin/tmux_copyrat.rs index bba6f79..df8b672 100644 --- a/src/bin/tmux_copyrat.rs +++ b/src/bin/tmux_copyrat.rs @@ -40,6 +40,10 @@ fn main() -> Result<(), error::ParseError> { output_destination, }) => { if uppercased { + if active_pane.is_copy_mode { + // break out of copy mode + duct::cmd!("tmux", "copy-mode", "-t", active_pane.id.as_str(), "-q").run()?; + } duct::cmd!("tmux", "send-keys", "-t", active_pane.id.as_str(), &text).run()?; } diff --git a/src/tmux.rs b/src/tmux.rs index fc15719..4e5c459 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -15,16 +15,16 @@ use crate::error::ParseError; pub struct Pane { /// Pane identifier, e.g. `%37`. pub id: PaneId, - /// Describes if the pane is in some mode. - pub in_mode: bool, + /// Describes if the pane is in copy mode. + pub is_copy_mode: bool, /// Number of lines in the pane. - pub height: u32, + pub height: i32, /// Optional offset from the bottom if the pane is in some mode. /// /// When a pane is in copy mode, scrolling up changes the /// `scroll_position`. If the pane is in normal mode, or unscrolled, /// then `0` is returned. - pub scroll_position: u32, + pub scroll_position: i32, /// Describes if the pane is currently active (focused). pub is_active: bool, } @@ -59,9 +59,9 @@ impl FromStr for Pane { // let id = id_str[1..].parse::()?; // let id = format!("%{}", id); - let in_mode = iter.next().unwrap().parse::()?; + let is_copy_mode = iter.next().unwrap().parse::()?; - let height = iter.next().unwrap().parse::()?; + let height = iter.next().unwrap().parse::()?; let scroll_position = iter.next().unwrap(); let scroll_position = if scroll_position.is_empty() { @@ -69,13 +69,13 @@ impl FromStr for Pane { } else { scroll_position }; - let scroll_position = scroll_position.parse::()?; + let scroll_position = scroll_position.parse::()?; let is_active = iter.next().unwrap().parse::()?; Ok(Pane { id, - in_mode, + is_copy_mode, height, scroll_position, is_active, @@ -167,35 +167,41 @@ pub fn get_options(prefix: &str) -> Result, ParseError> /// The provided `region` 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 -/// without extra arguments (default behavior of `capture-pane`), but if the -/// pane is in copy mode, we need to take into account the current scroll -/// position. To support both cases, the implementation always provides those -/// parameters to tmux. +/// In Tmux, the start line is the line at the top of the pane. The end line +/// is the last line at the bottom of the pane. +/// +/// - In normal mode, the index of the start line is always 0. The index of +/// the end line is always the pane's height minus one. These do not need to +/// be specified when capturing the pane's content. +/// +/// - If navigating history in copy mode, the index of the start line is the +/// opposite of the pane's scroll position. For instance a pane of 40 lines, +/// scrolled up by 3 lines. It is necessarily in copy mode. Its start line +/// index is `-3`. The index of the last line is `(40-1) - 3 = 36`. +/// pub fn capture_pane(pane: &Pane, region: &CaptureRegion) -> Result { - let mut args = format!("capture-pane -t {pane_id} -J -p", pane_id = pane.id); + let mut args_str = format!("capture-pane -t {pane_id} -J -p", pane_id = pane.id); let region_str = match region { CaptureRegion::VisibleArea => { - // Providing start/end helps support both copy and normal modes. - format!( - " -S {start} -E {end}", - start = pane.scroll_position, - end = pane.height - pane.scroll_position - 1 - ) + if pane.is_copy_mode && pane.scroll_position > 0 { + format!( + " -S {start} -E {end}", + start = -pane.scroll_position, + end = pane.height - pane.scroll_position - 1 + ) + } else { + String::new() + } } CaptureRegion::EntireHistory => String::from(" -S - -E -"), }; - args.push_str(®ion_str); + args_str.push_str(®ion_str); - let args: Vec<&str> = args.split(' ').collect(); + let args: Vec<&str> = args_str.split(' ').collect(); let output = duct::cmd("tmux", &args).read()?; Ok(output) @@ -226,7 +232,7 @@ mod tests { let expected = vec![ Pane { id: PaneId::from_str("%52").unwrap(), - in_mode: false, + is_copy_mode: false, height: 62, scroll_position: 3, is_active: false, @@ -234,7 +240,7 @@ mod tests { Pane { // id: PaneId::from_str("%53").unwrap(), id: PaneId(String::from("%53")), - in_mode: false, + is_copy_mode: false, height: 23, scroll_position: 0, is_active: true,