pub mod app;
pub mod highlight;
pub mod render_rows;
pub mod theme;
use anyhow::{Context, Result};
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use ratatui::prelude::*;
use std::io::stdout;
pub use app::WatchPaths;
use crate::comments::model::CommentStore;
use crate::diff::model::Changeset;
fn reattach_stdin_to_tty() -> Result<()> {
use std::os::fd::IntoRawFd;
let is_tty = |fd: i32| unsafe { libc::isatty(fd) } == 1;
if is_tty(libc::STDIN_FILENO) {
return Ok(());
}
if is_tty(libc::STDOUT_FILENO) || is_tty(libc::STDERR_FILENO) {
let src = if is_tty(libc::STDOUT_FILENO) {
libc::STDOUT_FILENO
} else {
libc::STDERR_FILENO
};
if unsafe { libc::dup2(src, libc::STDIN_FILENO) } < 0 {
return Err(std::io::Error::last_os_error())
.context("redirecting stdin to the inherited terminal");
}
return Ok(());
}
let tty = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/tty")
.context("opening /dev/tty to read interactive input")?;
let fd = tty.into_raw_fd();
let rc = unsafe { libc::dup2(fd, libc::STDIN_FILENO) };
let dup_err = std::io::Error::last_os_error();
unsafe { libc::close(fd) };
if rc < 0 {
return Err(dup_err).context("redirecting stdin to /dev/tty");
}
Ok(())
}
pub fn run(changeset: Changeset, comments: CommentStore, watch: Option<WatchPaths>) -> Result<()> {
if changeset.is_empty() && watch.is_none() {
println!("hew: no changes to review");
return Ok(());
}
reattach_stdin_to_tty()?;
enable_raw_mode()?;
let mut out = stdout();
execute!(out, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(out);
let mut terminal = Terminal::new(backend)?;
let mut app = app::App::with_comments(changeset, comments);
if let Some(w) = watch {
app = app.watching(w);
}
let result = app.run(&mut terminal);
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
result
}