feat: capture in copy mode

This commit is contained in:
graelo 2021-03-24 11:36:58 +01:00
parent 7550895602
commit b049fba642
2 changed files with 38 additions and 28 deletions

View file

@ -40,6 +40,10 @@ fn main() -> Result<(), error::ParseError> {
output_destination, output_destination,
}) => { }) => {
if uppercased { 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()?; duct::cmd!("tmux", "send-keys", "-t", active_pane.id.as_str(), &text).run()?;
} }

View file

@ -15,16 +15,16 @@ use crate::error::ParseError;
pub struct Pane { pub struct Pane {
/// Pane identifier, e.g. `%37`. /// Pane identifier, e.g. `%37`.
pub id: PaneId, pub id: PaneId,
/// Describes if the pane is in some mode. /// Describes if the pane is in copy mode.
pub in_mode: bool, pub is_copy_mode: bool,
/// Number of lines in the pane. /// Number of lines in the pane.
pub height: u32, pub height: i32,
/// Optional offset from the bottom if the pane is in some mode. /// Optional offset from the bottom if the pane is in some mode.
/// ///
/// When a pane is in copy mode, scrolling up changes the /// When a pane is in copy mode, scrolling up changes the
/// `scroll_position`. If the pane is in normal mode, or unscrolled, /// `scroll_position`. If the pane is in normal mode, or unscrolled,
/// then `0` is returned. /// then `0` is returned.
pub scroll_position: u32, pub scroll_position: i32,
/// Describes if the pane is currently active (focused). /// Describes if the pane is currently active (focused).
pub is_active: bool, pub is_active: bool,
} }
@ -59,9 +59,9 @@ impl FromStr for Pane {
// let id = id_str[1..].parse::<u32>()?; // let id = id_str[1..].parse::<u32>()?;
// let id = format!("%{}", id); // let id = format!("%{}", id);
let in_mode = iter.next().unwrap().parse::<bool>()?; let is_copy_mode = iter.next().unwrap().parse::<bool>()?;
let height = iter.next().unwrap().parse::<u32>()?; let height = iter.next().unwrap().parse::<i32>()?;
let scroll_position = iter.next().unwrap(); let scroll_position = iter.next().unwrap();
let scroll_position = if scroll_position.is_empty() { let scroll_position = if scroll_position.is_empty() {
@ -69,13 +69,13 @@ impl FromStr for Pane {
} else { } else {
scroll_position scroll_position
}; };
let scroll_position = scroll_position.parse::<u32>()?; let scroll_position = scroll_position.parse::<i32>()?;
let is_active = iter.next().unwrap().parse::<bool>()?; let is_active = iter.next().unwrap().parse::<bool>()?;
Ok(Pane { Ok(Pane {
id, id,
in_mode, is_copy_mode,
height, height,
scroll_position, scroll_position,
is_active, is_active,
@ -167,35 +167,41 @@ pub fn get_options(prefix: &str) -> Result<HashMap<String, String>, ParseError>
/// The provided `region` specifies if the visible area is captured, or the /// The provided `region` specifies if the visible area is captured, or the
/// entire history. /// entire history.
/// ///
/// # TODO
///
/// Capture with `capture-pane -J` joins wrapped lines.
///
/// # Note /// # Note
/// ///
/// If the pane is in normal mode, capturing the visible area can be done /// In Tmux, the start line is the line at the top of the pane. The end line
/// without extra arguments (default behavior of `capture-pane`), but if the /// is the last line at the bottom of the pane.
/// pane is in copy mode, we need to take into account the current scroll ///
/// position. To support both cases, the implementation always provides those /// - In normal mode, the index of the start line is always 0. The index of
/// parameters to tmux. /// 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<String, ParseError> { pub fn capture_pane(pane: &Pane, region: &CaptureRegion) -> Result<String, ParseError> {
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 { let region_str = match region {
CaptureRegion::VisibleArea => { CaptureRegion::VisibleArea => {
// Providing start/end helps support both copy and normal modes. if pane.is_copy_mode && pane.scroll_position > 0 {
format!( format!(
" -S {start} -E {end}", " -S {start} -E {end}",
start = pane.scroll_position, start = -pane.scroll_position,
end = pane.height - pane.scroll_position - 1 end = pane.height - pane.scroll_position - 1
) )
} else {
String::new()
}
} }
CaptureRegion::EntireHistory => String::from(" -S - -E -"), CaptureRegion::EntireHistory => String::from(" -S - -E -"),
}; };
args.push_str(&region_str); args_str.push_str(&region_str);
let args: Vec<&str> = args.split(' ').collect(); let args: Vec<&str> = args_str.split(' ').collect();
let output = duct::cmd("tmux", &args).read()?; let output = duct::cmd("tmux", &args).read()?;
Ok(output) Ok(output)
@ -226,7 +232,7 @@ mod tests {
let expected = vec![ let expected = vec![
Pane { Pane {
id: PaneId::from_str("%52").unwrap(), id: PaneId::from_str("%52").unwrap(),
in_mode: false, is_copy_mode: false,
height: 62, height: 62,
scroll_position: 3, scroll_position: 3,
is_active: false, is_active: false,
@ -234,7 +240,7 @@ mod tests {
Pane { Pane {
// id: PaneId::from_str("%53").unwrap(), // id: PaneId::from_str("%53").unwrap(),
id: PaneId(String::from("%53")), id: PaneId(String::from("%53")),
in_mode: false, is_copy_mode: false,
height: 23, height: 23,
scroll_position: 0, scroll_position: 0,
is_active: true, is_active: true,