Skip to content

Commit

Permalink
tmux support
Browse files Browse the repository at this point in the history
  • Loading branch information
eugene-babichenko committed Jun 12, 2024
1 parent 80112c7 commit 7b0e8b0
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 49 deletions.
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ See contributing guidelines [here](CONTRIBUTING.md).
When you run the `fix` command, it gets the last command from the shell history.
Then one of two things happen:

- It gets the output of the last command via your terminal emulator API
(available on WezTerm).
- It gets the output of the last command via your terminal emulator/multiplexer
API (available on WezTerm and tmux).
- Or it simply re-runs the command to get its output.

Once `fixit` has the command output, it runs the command and its output through
Expand Down Expand Up @@ -68,14 +68,15 @@ Environment variables:

WezTerm specific:

- `FIXIT_WEZTERM_ENABLE` - when running inside WezTerm, try to get the command
output with `wezterm get-text` instead of re-running the given command. This
is generally much faster, so it is recommended that you leave it as is unless
you run into any bugs associated with running inside WezTerm. The default
value is `true`. Pass `false` to disable.
- `FIXIT_WEZTERM_SEARCH_DEPTH` sets the number of lines to get from the WezTerm
scrollback buffer in addition to what we see on the screen. The default is
`1000`.
- `FIXIT_QUIT_ENABLE` - when running inside a supported terminal
emulator/multiplexer, try to get the command output with its API instead of
re-running the given command. This is generally much faster, so it is
recommended that you leave it as is unless you run into any bugs associated
with finding fixes. The combination that can be potentially buggy is
suppported terminal emulator with unsupported multiplexer. The default value
is `true`. Pass `false` to disable.
- `FIXIT_QUICK_SEARCH_DEPTH` sets the number of lines to get from the scrollback
buffer in addition to what we see on the screen. The default is `1000`.

### Logging

Expand Down
118 changes: 79 additions & 39 deletions src/fix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ pub struct Cmd {
#[arg(env = "FIXIT_PAGE_SIZE", default_value_t = 5)]
page_size: usize,
#[command(flatten)]
wezterm: WezTerm,
quick: QuickOutput,
}

#[derive(Parser)]
struct WezTerm {
struct QuickOutput {
/// Enable searching via WezTerm API
#[arg(env = "FIXIT_WEZTERM_ENABLE", default_value_t = true)]
#[arg(env = "FIXIT_QUICK_ENABLE", default_value_t = true)]
enable: bool,
/// The number of lines to scan from the WezTerm scrollback buffer.
#[arg(env = "FIXIT_WEZTERM_SEARCH_DEPTH", default_value_t = 1000)]
#[arg(env = "FIXIT_QUICK_SEARCH_DEPTH", default_value_t = 1000)]
depth: usize,
}

Expand All @@ -61,10 +61,24 @@ impl Cmd {
let time = SystemTime::now();

// First we try to extract the output of the command without re-running it.
let output = if let Some(output) = get_text_wezterm(&self.cmd, self.wezterm) {
log::debug!("got output from wezterm: {output}");
// Future note: terminal multiplexers must be checked before terminal
// emulators.
let output = if self.quick.enable {
if let Some(output) = self.get_text_tmux() {
log::debug!("got output from tmux");
Some(output)
} else if let Some(output) = self.get_text_wezterm() {
log::debug!("got output from wezterm");
Some(output)
} else {
None
}
} else {
None
};
let output = if let Some(output) = output {
vec![output]
} else if let Some((stderr, stdout)) = get_text_rerun(&self.cmd)? {
} else if let Some((stderr, stdout)) = self.get_text_rerun()? {
// If we fail at this, we just re-run it.
log::debug!("re-ran the command");
vec![stderr, stdout]
Expand Down Expand Up @@ -153,59 +167,85 @@ impl Cmd {

Ok(())
}
}

fn get_text_rerun(cmd: &str) -> Result<Option<(String, String)>, Error> {
// re-run the command in the current shell
let shell = env::var("SHELL")?;
fn get_text_rerun(&self) -> Result<Option<(String, String)>, Error> {
// re-run the command in the current shell
let shell = env::var("SHELL")?;

log::debug!("re-running command: {}", &self.cmd);
let output = Command::new(shell).arg("-c").arg(&self.cmd).output()?;

// if the command is successful we have nothing to do
if output.status.success() {
eprintln!("The command ran successfully: nothing to fix.");
return Ok(None);
}

let stderr = String::from_utf8(output.stderr)?;
let stdout = String::from_utf8(output.stdout)?;

log::debug!("re-running command: {}", cmd);
let output = Command::new(shell).arg("-c").arg(cmd).output()?;
log::debug!("command stderr: {}", stderr);
log::debug!("command stdout: {}", stdout);

// if the command is successful we have nothing to do
if output.status.success() {
eprintln!("The command ran successfully: nothing to fix.");
return Ok(None);
Ok(Some((stderr, stdout)))
}

let stderr = String::from_utf8(output.stderr)?;
let stdout = String::from_utf8(output.stdout)?;
fn get_text_wezterm(&self) -> Option<String> {
let wezterm = env::var("WEZTERM_EXECUTABLE").ok()?;

log::debug!("command stderr: {}", stderr);
log::debug!("command stdout: {}", stdout);
log::debug!("getting the command output from WezTerm");

Ok(Some((stderr, stdout)))
}
let wezterm = wezterm.strip_suffix("-gui").unwrap_or(&wezterm);

fn get_text_wezterm(cmd: &str, config: WezTerm) -> Option<String> {
let WezTerm { depth, enable } = config;
let output = Command::new(wezterm)
.args([
"cli",
"get-text",
"--start-line",
&format!("-{}", self.quick.depth),
])
.output()
.map_err(|e| log::error!("failed to get output from WezTerm: {e}"))
.ok()?;

if !enable {
return None;
if !output.status.success() {
return None;
}

find_command_outut(&self.cmd, output.stdout)
}

let wezterm = env::var("WEZTERM_EXECUTABLE").ok()?;
fn get_text_tmux(&self) -> Option<String> {
env::var("TMUX").ok()?;

log::debug!("getting the command output from WezTerm");
log::debug!("getting the command output from tmux");

let wezterm = wezterm.strip_suffix("-gui").unwrap_or(&wezterm);
let output = Command::new("tmux")
.args([
"capture-pane",
"-p",
"-S",
&format!("-{}", self.quick.depth),
])
.output()
.map_err(|e| log::error!("failed to get output from tmux: {e}"))
.ok()?;

let output = Command::new(wezterm)
.args(["cli", "get-text", "--start-line", &format!("-{depth}")])
.output()
.map_err(|e| log::error!("failed to get output from WezTerm: {e}"))
.ok()?;
if !output.status.success() {
return None;
}

if !output.status.success() {
return None;
find_command_outut(&self.cmd, output.stdout)
}
}

let stdout = String::from_utf8(output.stdout)
fn find_command_outut(cmd: &str, stdout: Vec<u8>) -> Option<String> {
let stdout = String::from_utf8(stdout)
.map_err(|e| log::error!("failed to stringify output from WezTerm: {e}"))
.ok()?;

if !stdout.contains(cmd) {
log::debug!("command not found in WezTerm output");
log::debug!("command not found in stdout");
return None;
}

Expand Down

0 comments on commit 7b0e8b0

Please sign in to comment.