2024-10-02 14:56:07 -04:00
|
|
|
use std::hint::unreachable_unchecked;
|
2024-10-02 12:28:14 -04:00
|
|
|
use std::io::Error;
|
2024-10-02 14:56:07 -04:00
|
|
|
use std::process::Command as Executable;
|
2024-10-02 12:28:14 -04:00
|
|
|
|
|
|
|
use crossterm::event::KeyCode;
|
|
|
|
use tui::layout::Rect;
|
|
|
|
|
2024-10-02 14:56:07 -04:00
|
|
|
use crate::command::Command;
|
|
|
|
use crate::command_output::CommandOutput;
|
2024-10-02 12:28:14 -04:00
|
|
|
use crate::directory::Directory;
|
2024-10-02 14:56:07 -04:00
|
|
|
use crate::pane::{Message, Pane};
|
2024-10-02 12:28:14 -04:00
|
|
|
use crate::terminal::Terminal;
|
|
|
|
|
2024-10-02 14:56:07 -04:00
|
|
|
enum Either<T, U> {
|
|
|
|
Cmd(T),
|
|
|
|
CmdOut(U),
|
|
|
|
}
|
|
|
|
|
2024-10-02 12:28:14 -04:00
|
|
|
pub struct State {
|
2024-10-02 14:56:07 -04:00
|
|
|
dir: Directory,
|
|
|
|
size: Rect,
|
|
|
|
command: Option<Either<Command, CommandOutput>>
|
2024-10-02 12:28:14 -04:00
|
|
|
}
|
|
|
|
impl State {
|
2024-10-02 16:04:11 -04:00
|
|
|
pub fn new(area: Rect) -> Result<Self, Error> {
|
|
|
|
let dir = Directory::new("root".into())?;
|
|
|
|
Ok(Self {
|
2024-10-02 14:56:07 -04:00
|
|
|
dir,
|
|
|
|
size: area,
|
|
|
|
command: None,
|
2024-10-02 16:04:11 -04:00
|
|
|
})
|
2024-10-02 12:28:14 -04:00
|
|
|
}
|
|
|
|
|
2024-10-02 14:56:07 -04:00
|
|
|
pub fn render(&self, terminal: &mut Terminal) -> Result<(), Error> {
|
|
|
|
let command_size = match &self.command {
|
|
|
|
None => 0,
|
|
|
|
Some(e) => match e {
|
|
|
|
Either::Cmd(c) => c.lines_hint(),
|
|
|
|
Either::CmdOut(o) => o.lines_hint(),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let (dir_area, bottom_area) = if command_size > 0 {
|
|
|
|
(Rect {
|
|
|
|
height: self.size.height - command_size as u16 - 2,
|
|
|
|
..self.size
|
|
|
|
}, Rect {
|
|
|
|
y: self.size.height - command_size as u16 - 2,
|
|
|
|
height: command_size as u16 + 2,
|
|
|
|
..self.size
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
(self.size, self.size)
|
|
|
|
};
|
|
|
|
|
2024-10-02 12:28:14 -04:00
|
|
|
terminal.draw(
|
2024-10-02 14:56:07 -04:00
|
|
|
|output| {
|
|
|
|
self.dir.display(output, dir_area);
|
|
|
|
if let Some(command) = self.command.as_ref() {
|
|
|
|
match command {
|
|
|
|
Either::Cmd(cmd) => cmd.display(output, bottom_area),
|
|
|
|
Either::CmdOut(cmd) => cmd.display(output, bottom_area),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-10-02 12:28:14 -04:00
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-10-02 14:56:07 -04:00
|
|
|
const fn current_pane(&self) -> &dyn Pane {
|
|
|
|
match self.command.as_ref() {
|
|
|
|
None => &self.dir,
|
|
|
|
Some(either) => match either {
|
|
|
|
Either::Cmd(c) => c,
|
|
|
|
Either::CmdOut(o) => o,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-02 12:28:14 -04:00
|
|
|
pub fn cursor_position(&self) -> Option<(u16, u16)> {
|
2024-10-02 14:56:07 -04:00
|
|
|
let pane = self.current_pane();
|
|
|
|
|
|
|
|
let (x, y) = pane.cursor_position()?;
|
|
|
|
let new_x = x + 1;
|
|
|
|
let dy = if let Some(Either::Cmd(c)) = &self.command {
|
|
|
|
self.size.height - c.lines_hint() as u16 - 2
|
|
|
|
} else {
|
|
|
|
self.size.y
|
|
|
|
};
|
|
|
|
let new_y = y + dy + 1;
|
|
|
|
|
|
|
|
Some((new_x, new_y))
|
2024-10-02 12:28:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update(&mut self, key: KeyCode) -> Message {
|
2024-10-02 14:56:07 -04:00
|
|
|
match self.command.as_mut() {
|
|
|
|
None => self.dir.update(key),
|
|
|
|
Some(either) => match either {
|
|
|
|
Either::Cmd(c) => c.update(key),
|
|
|
|
Either::CmdOut(_) => { self.close_window(); Message::Nothing },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn begin_command(&mut self) {
|
|
|
|
self.command = Some(Either::Cmd(Command::new()));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn execute_command(&mut self) {
|
|
|
|
let Some(Either::Cmd(cmd)) = self.command.take()
|
|
|
|
else { unsafe { unreachable_unchecked() } };
|
|
|
|
|
|
|
|
let mut iter = cmd.buf()
|
2024-10-02 16:04:11 -04:00
|
|
|
.split(' ')
|
|
|
|
.filter(|s| !s.is_empty());
|
2024-10-02 14:56:07 -04:00
|
|
|
|
|
|
|
let prog_name = iter.next().unwrap();
|
|
|
|
let output = Executable::new(prog_name)
|
|
|
|
.args(iter)
|
2024-10-02 16:04:11 -04:00
|
|
|
.arg(self.dir.path())
|
2024-10-02 14:56:07 -04:00
|
|
|
.output();
|
|
|
|
|
|
|
|
let command_output = CommandOutput::new(output.map(|o| o.stdout));
|
|
|
|
if !command_output.should_display() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.command = Some(Either::CmdOut(command_output));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cancel_command(&mut self) {
|
|
|
|
self.command = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn close_window(&mut self) {
|
|
|
|
self.command = None;
|
2024-10-02 12:28:14 -04:00
|
|
|
}
|
|
|
|
}
|