Massive refactor using channels to improve typing speed
This commit is contained in:
parent
df9c53b20d
commit
8d26f6c55c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -297,6 +297,7 @@ dependencies = [
|
|||||||
"atty",
|
"atty",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"parking_lot",
|
||||||
"shellwords",
|
"shellwords",
|
||||||
"tui",
|
"tui",
|
||||||
]
|
]
|
||||||
|
@ -13,3 +13,4 @@ ansi4tui = {path = "./ansi4tui/"}
|
|||||||
anyhow = "1.0.66"
|
anyhow = "1.0.66"
|
||||||
shellwords = "1.1.0"
|
shellwords = "1.1.0"
|
||||||
crossbeam = "0.8.2"
|
crossbeam = "0.8.2"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
use crate::event::EventMessage;
|
use crate::event::EventMessage;
|
||||||
|
use crate::CommandOptions;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossbeam::channel::Receiver;
|
use crossbeam::channel::Receiver;
|
||||||
use crossbeam::channel::Sender;
|
use crossbeam::channel::Sender;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type CommandRequest = (Arc<RwLock<CommandOptions>>, Arc<String>);
|
||||||
use crate::App;
|
use crate::App;
|
||||||
|
|
||||||
pub struct CommandRequest {
|
pub enum CommandCompleted {
|
||||||
cmdline: String,
|
Success(CommandResult),
|
||||||
|
Failure(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandResult {
|
pub struct CommandResult {
|
||||||
@ -31,46 +36,65 @@ impl Default for CommandResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn command_event_loop(command_request_receiver: Receiver<CommandRequest>,event_sender: Sender<EventMessage>) -> Result<()> {
|
pub fn command_event_loop(
|
||||||
// loop {
|
command_request_receiver: Receiver<CommandRequest>,
|
||||||
// match command_request_receiver.recv() {
|
event_sender: Sender<EventMessage>,
|
||||||
// Ok(command_request) => event_sender.send(EventMessage::CrosstermEvent(e))?,
|
) -> Result<()> {
|
||||||
// Err(e) => {
|
loop {
|
||||||
// event_sender.send(EventMessage::CrosstermError(e))?;
|
match command_request_receiver.recv() {
|
||||||
// bail!("Crossterm read error");
|
Ok(command_request) => {
|
||||||
// }
|
event_sender.send(EventMessage::CommandCompleted(
|
||||||
// }
|
match run_inner(command_request) {
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn run(app: &mut App) {
|
|
||||||
match run_inner(app) {
|
|
||||||
Ok(c) => {
|
Ok(c) => {
|
||||||
// If there was no stdout and the command failed, don't touch stdout
|
// If there was no stdout and the command failed, don't touch stdout
|
||||||
if !c.status_success && c.stdout.is_empty() {
|
if !c.status_success && c.stdout.is_empty() {
|
||||||
app.command_result.stderr = c.stderr;
|
CommandCompleted::Failure(c.stderr)
|
||||||
app.command_result.status_success = c.status_success;
|
|
||||||
} else {
|
} else {
|
||||||
app.command_result = c;
|
CommandCompleted::Success(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => CommandCompleted::Failure(e.to_string().as_bytes().to_vec()),
|
||||||
|
},
|
||||||
|
))?;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
app.command_result.status_success = false;
|
event_sender.send(EventMessage::CommandLoopStopped)?;
|
||||||
app.command_result.stderr = e.to_string().as_bytes().to_vec();
|
bail!("Crossterm read error: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_inner(app: &mut App) -> Result<CommandResult> {
|
// pub fn run(app: Arc<App>) {
|
||||||
let mut command = Command::new(&app.command);
|
// match run_inner(app) {
|
||||||
|
// Ok(c) => {
|
||||||
|
// // If there was no stdout and the command failed, don't touch stdout
|
||||||
|
// if !c.status_success && c.stdout.is_empty() {
|
||||||
|
// app.command_result.stderr = c.stderr;
|
||||||
|
// app.command_result.status_success = c.status_success;
|
||||||
|
// } else {
|
||||||
|
// app.command_result = c;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Err(e) => {
|
||||||
|
// app.command_result.status_success = false;
|
||||||
|
// app.command_result.stderr = e.to_string().as_bytes().to_vec();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn run_inner(command_request: CommandRequest) -> Result<CommandResult> {
|
||||||
|
let request = command_request.0.read();
|
||||||
|
|
||||||
|
let mut command = Command::new(&request.command);
|
||||||
command
|
command
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.args(&app.hidden_options);
|
.args(&request.hidden_options);
|
||||||
|
|
||||||
if app.wordsplit {
|
if request.wordsplit {
|
||||||
match shellwords::split(&app.cmdline) {
|
match shellwords::split(&request.cmdline) {
|
||||||
Ok(a) => {
|
Ok(a) => {
|
||||||
command.args(a);
|
command.args(a);
|
||||||
}
|
}
|
||||||
@ -80,18 +104,18 @@ fn run_inner(app: &mut App) -> Result<CommandResult> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: Avoid cloning here
|
// TODO: Avoid cloning here
|
||||||
command.arg(&app.cmdline);
|
command.arg(&request.cmdline);
|
||||||
}
|
}
|
||||||
let mut child = command.spawn().context("Could not spawn child process")?;
|
let mut child = command.spawn()?;
|
||||||
|
|
||||||
let mut stdin = child.stdin.take().context("Could not take stdin")?;
|
let mut stdin = child.stdin.take().context("Could not take stdin")?;
|
||||||
let text_orig_clone = app.text_orig.clone();
|
let text_orig_clone = command_request.1.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let _result = stdin.write_all(text_orig_clone.as_bytes());
|
let _result = stdin.write_all(text_orig_clone.as_bytes());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collect the output
|
// Collect the output
|
||||||
let output = child.wait_with_output().context("Failed to read stdout")?;
|
let output = child.wait_with_output()?;
|
||||||
|
|
||||||
Ok(CommandResult {
|
Ok(CommandResult {
|
||||||
status_success: output.status.success(),
|
status_success: output.status.success(),
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::command::CommandRequest;
|
use crate::command::CommandCompleted;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossbeam::channel::Sender;
|
use crossbeam::channel::Sender;
|
||||||
|
|
||||||
pub enum EventMessage {
|
pub enum EventMessage {
|
||||||
CommandCompleted,
|
CommandCompleted(CommandCompleted),
|
||||||
CrosstermEvent(crossterm::event::Event),
|
CrosstermEvent(crossterm::event::Event),
|
||||||
CrosstermError(std::io::Error),
|
CrosstermError(std::io::Error),
|
||||||
|
CommandLoopStopped,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crossterm_event_loop(event_sender: Sender<EventMessage>) -> Result<()> {
|
pub fn crossterm_event_loop(event_sender: Sender<EventMessage>) -> Result<()> {
|
||||||
|
333
src/main.rs
333
src/main.rs
@ -8,11 +8,16 @@ mod ui;
|
|||||||
use ansi4tui::bytes_to_text;
|
use ansi4tui::bytes_to_text;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use command::CommandRequest;
|
||||||
use command::CommandResult;
|
use command::CommandResult;
|
||||||
|
use crossbeam::channel::Receiver;
|
||||||
|
use crossbeam::channel::Sender;
|
||||||
use crossterm::event::DisableBracketedPaste;
|
use crossterm::event::DisableBracketedPaste;
|
||||||
use crossterm::event::EnableBracketedPaste;
|
use crossterm::event::EnableBracketedPaste;
|
||||||
use event::EventMessage;
|
use event::EventMessage;
|
||||||
|
use parking_lot::RwLock;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
@ -35,8 +40,8 @@ use crossterm::{
|
|||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The state of the application
|
/// The state of options for the command
|
||||||
pub struct App {
|
pub struct CommandOptions {
|
||||||
/// The actual command to be called
|
/// The actual command to be called
|
||||||
command: String,
|
command: String,
|
||||||
|
|
||||||
@ -46,94 +51,109 @@ pub struct App {
|
|||||||
/// The line the user inputs
|
/// The line the user inputs
|
||||||
cmdline: String,
|
cmdline: String,
|
||||||
|
|
||||||
/// The position of the cursor
|
|
||||||
cmdline_position: u16,
|
|
||||||
|
|
||||||
/// Original text
|
|
||||||
text_orig: Arc<String>,
|
|
||||||
|
|
||||||
/// The result of a command execution
|
|
||||||
command_result: CommandResult,
|
|
||||||
|
|
||||||
/// Should every keystroke transform the original text?
|
/// Should every keystroke transform the original text?
|
||||||
autorun: bool,
|
autorun: bool,
|
||||||
|
|
||||||
/// Should wordsplitting be enabled
|
/// Should wordsplitting be enabled
|
||||||
wordsplit: bool,
|
wordsplit: bool,
|
||||||
|
|
||||||
|
/// The result of a command execution
|
||||||
|
command_result: CommandResult,
|
||||||
|
|
||||||
|
/// The position of the cursor
|
||||||
|
cmdline_position: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state of the application
|
||||||
|
pub struct App {
|
||||||
|
/// Original text
|
||||||
|
text_orig: Arc<String>,
|
||||||
|
|
||||||
|
// text_orig_formatted: Arc<String>
|
||||||
|
/// The list of options for a command, given in an RwLock so the command runner can update it
|
||||||
|
command_options: Arc<RwLock<CommandOptions>>,
|
||||||
|
|
||||||
|
/// The receiver for events
|
||||||
|
message_rx: Receiver<EventMessage>,
|
||||||
|
|
||||||
|
/// The command request transmitter
|
||||||
|
command_request_tx: Sender<CommandRequest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandOptions {
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_template(template: &Template) -> Self {
|
||||||
|
let defaults = Self {
|
||||||
|
command: template.command(),
|
||||||
|
cmdline_position: 0_u16,
|
||||||
|
hidden_options: Vec::new(),
|
||||||
|
cmdline: String::new(),
|
||||||
|
command_result: CommandResult::default(),
|
||||||
|
autorun: true,
|
||||||
|
wordsplit: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
match template {
|
||||||
|
Template::Awk => Self { ..defaults },
|
||||||
|
Template::Sh(_) => Self {
|
||||||
|
hidden_options: vec!["-c"],
|
||||||
|
wordsplit: false,
|
||||||
|
..defaults
|
||||||
|
},
|
||||||
|
Template::Jq => Self {
|
||||||
|
cmdline_position: 2,
|
||||||
|
cmdline: String::from("'.'"),
|
||||||
|
hidden_options: vec!["-C"],
|
||||||
|
..defaults
|
||||||
|
},
|
||||||
|
Template::Grep | Template::Rg => Self {
|
||||||
|
cmdline_position: 1,
|
||||||
|
cmdline: String::from("''"),
|
||||||
|
hidden_options: vec!["--color=always"],
|
||||||
|
..defaults
|
||||||
|
},
|
||||||
|
Template::Sed => Self {
|
||||||
|
cmdline_position: 3_u16,
|
||||||
|
cmdline: String::from("'s///g'"),
|
||||||
|
..defaults
|
||||||
|
},
|
||||||
|
Template::Perl => Self {
|
||||||
|
cmdline_position: 10_u16,
|
||||||
|
cmdline: String::from("-p -e 's///'"),
|
||||||
|
..defaults
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
/// Constructs a new instance of `App`
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
///
|
||||||
|
/// * `command_request_tx` - The sender for the command request worker
|
||||||
|
/// * `input` - The stdin to be passed in to each invocation
|
||||||
|
/// * `template` - The template to use
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_template(input: String, template: &Template) -> Self {
|
pub fn from_template(
|
||||||
let text_orig = Arc::new(input);
|
message_rx: Receiver<EventMessage>,
|
||||||
let command_result = CommandResult::default();
|
command_request_tx: Sender<CommandRequest>,
|
||||||
let command = template.command();
|
input: String,
|
||||||
let cmdline_position = 0;
|
template: &Template,
|
||||||
let wordsplit = true;
|
) -> Self {
|
||||||
|
Self {
|
||||||
match template {
|
text_orig: Arc::new(input),
|
||||||
Template::Awk => Self {
|
command_options: Arc::new(RwLock::new(CommandOptions::from_template(template))),
|
||||||
cmdline: String::new(),
|
message_rx,
|
||||||
cmdline_position,
|
command_request_tx,
|
||||||
text_orig,
|
|
||||||
command_result: CommandResult::default(),
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec![],
|
|
||||||
wordsplit,
|
|
||||||
},
|
|
||||||
Template::Sh(_) => Self {
|
|
||||||
cmdline: String::from(""),
|
|
||||||
cmdline_position: 0,
|
|
||||||
text_orig,
|
|
||||||
command_result,
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec!["-c"],
|
|
||||||
wordsplit: false,
|
|
||||||
},
|
|
||||||
Template::Jq => Self {
|
|
||||||
cmdline: String::from("'.'"),
|
|
||||||
cmdline_position: 2,
|
|
||||||
text_orig,
|
|
||||||
command_result,
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec!["-C"],
|
|
||||||
wordsplit,
|
|
||||||
},
|
|
||||||
Template::Grep | Template::Rg => Self {
|
|
||||||
cmdline: String::from("''"),
|
|
||||||
cmdline_position: 1,
|
|
||||||
text_orig,
|
|
||||||
command_result: CommandResult::default(),
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec!["--color=always"],
|
|
||||||
wordsplit,
|
|
||||||
},
|
|
||||||
Template::Sed => Self {
|
|
||||||
cmdline: String::from("'s///g'"),
|
|
||||||
cmdline_position: 3_u16,
|
|
||||||
text_orig,
|
|
||||||
command_result: CommandResult::default(),
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec![],
|
|
||||||
wordsplit,
|
|
||||||
},
|
|
||||||
Template::Perl => Self {
|
|
||||||
cmdline: String::from("-p -e 's///'"),
|
|
||||||
cmdline_position: 10_u16,
|
|
||||||
text_orig,
|
|
||||||
command_result: CommandResult::default(),
|
|
||||||
autorun: true,
|
|
||||||
command,
|
|
||||||
hidden_options: vec![],
|
|
||||||
wordsplit,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_command(&self) -> Result<()> {
|
||||||
|
self.command_request_tx
|
||||||
|
.send((self.command_options.clone(), self.text_orig.clone()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Template {
|
pub enum Template {
|
||||||
@ -192,8 +212,16 @@ fn main() -> Result<()> {
|
|||||||
// Slurp all input
|
// Slurp all input
|
||||||
let text_orig = io::read_to_string(io::stdin())?;
|
let text_orig = io::read_to_string(io::stdin())?;
|
||||||
|
|
||||||
|
// Start the command worker thread
|
||||||
|
let (message_rx, command_request_tx) = init_message_passing();
|
||||||
|
|
||||||
// Run the actual application
|
// Run the actual application
|
||||||
let app = App::from_template(text_orig, &Template::from_str(&arg)?);
|
let app = App::from_template(
|
||||||
|
message_rx,
|
||||||
|
command_request_tx,
|
||||||
|
text_orig,
|
||||||
|
&Template::from_str(&arg)?,
|
||||||
|
);
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
// execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
// execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||||
@ -229,103 +257,148 @@ fn main() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_app<B: Backend>(
|
fn init_message_passing() -> (Receiver<EventMessage>, Sender<CommandRequest>) {
|
||||||
terminal: &mut Terminal<B>,
|
let (message_tx, message_rx) = crossbeam::channel::bounded(100);
|
||||||
mut app: App,
|
let (command_request_tx, command_request_rx) = crossbeam::channel::bounded(100);
|
||||||
) -> Result<Option<(String, Vec<u8>)>> {
|
|
||||||
if !app.cmdline.is_empty() {
|
|
||||||
command::run(&mut app);
|
|
||||||
}
|
|
||||||
|
|
||||||
if app.cmdline_position == 0 {
|
|
||||||
app.cmdline_position = app.cmdline.len() as u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (tx, rx) = crossbeam::channel::bounded(100);
|
|
||||||
|
|
||||||
// Start the event thread
|
// Start the event thread
|
||||||
let crossterm_tx = tx.clone();
|
let crossterm_message_tx = message_tx.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let _result = event::crossterm_event_loop(crossterm_tx);
|
let _result = event::crossterm_event_loop(crossterm_message_tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::mem::drop(tx);
|
// Start the worker thread
|
||||||
|
let command_message_tx = message_tx.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let _result = command::command_event_loop(command_request_rx, command_message_tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::mem::drop(message_tx);
|
||||||
|
|
||||||
|
(message_rx, command_request_tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: App) -> Result<Option<(String, Vec<u8>)>> {
|
||||||
|
{
|
||||||
|
let mut command_options = app.command_options.write();
|
||||||
|
|
||||||
|
if !command_options.cmdline.is_empty() {
|
||||||
|
app.run_command()?;
|
||||||
|
// command_request_tx.send((app.command_options.clone(), app.text_orig.clone()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if command_options.cmdline_position == 0 {
|
||||||
|
command_options.cmdline_position = command_options.cmdline.len() as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| ui::draw(f, &app))?;
|
terminal.draw(|f| ui::draw(f, &app))?;
|
||||||
|
|
||||||
match rx.recv()? {
|
match app.message_rx.recv()? {
|
||||||
// match event::read()? {
|
EventMessage::CrosstermEvent(crossterm_event) => {
|
||||||
EventMessage::CrosstermEvent(Event::Key(key)) => match key.code {
|
let mut command_options = app.command_options.write();
|
||||||
|
|
||||||
|
match crossterm_event {
|
||||||
|
Event::Key(key) => match key.code {
|
||||||
KeyCode::Esc => {
|
KeyCode::Esc => {
|
||||||
// TODO: If there is any command line text, ask if the user is sure they want to quit
|
// TODO: If there is any command line text, ask if the user is sure they want to quit
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
app.cmdline.insert(app.cmdline_position as usize, c);
|
let cmdline_position = command_options.cmdline_position as usize;
|
||||||
app.cmdline_position = app.cmdline_position.saturating_add(1);
|
command_options.cmdline.insert(cmdline_position, c);
|
||||||
if app.autorun {
|
command_options.cmdline_position =
|
||||||
command::run(&mut app);
|
command_options.cmdline_position.saturating_add(1);
|
||||||
|
|
||||||
|
if command_options.autorun {
|
||||||
|
app.run_command()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Backspace => {
|
KeyCode::Backspace => {
|
||||||
if app.cmdline_position > 0 {
|
if command_options.cmdline_position > 0 {
|
||||||
app.cmdline_position = app.cmdline_position.saturating_sub(1);
|
command_options.cmdline_position =
|
||||||
app.cmdline.remove(app.cmdline_position as usize);
|
command_options.cmdline_position.saturating_sub(1);
|
||||||
|
|
||||||
|
let cmdline_position = command_options.cmdline_position as usize;
|
||||||
|
command_options.cmdline.remove(cmdline_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.autorun {
|
if command_options.autorun {
|
||||||
command::run(&mut app);
|
app.run_command()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Delete => {
|
KeyCode::Delete => {
|
||||||
if (app.cmdline_position as usize) < app.cmdline.len() {
|
let cmdline_position = command_options.cmdline_position as usize;
|
||||||
app.cmdline.remove(app.cmdline_position as usize);
|
if (cmdline_position) < command_options.cmdline.len() {
|
||||||
|
command_options.cmdline.remove(cmdline_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.autorun {
|
if command_options.autorun {
|
||||||
command::run(&mut app);
|
app.run_command()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::End => {
|
KeyCode::End => {
|
||||||
app.cmdline_position = app.cmdline.len() as u16;
|
command_options.cmdline_position = command_options.cmdline.len() as u16;
|
||||||
}
|
}
|
||||||
KeyCode::Home => {
|
KeyCode::Home => {
|
||||||
app.cmdline_position = 0_u16;
|
command_options.cmdline_position = 0_u16;
|
||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
|
// TODO: Do not clone here
|
||||||
return Ok(Some((
|
return Ok(Some((
|
||||||
format!("{} {}", app.command, app.cmdline),
|
format!("{} {}", command_options.command, command_options.cmdline),
|
||||||
app.command_result.stdout,
|
command_options.command_result.stdout.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
KeyCode::Left => {
|
KeyCode::Left => {
|
||||||
app.cmdline_position = app.cmdline_position.saturating_sub(1);
|
command_options.cmdline_position =
|
||||||
|
command_options.cmdline_position.saturating_sub(1);
|
||||||
}
|
}
|
||||||
KeyCode::Right => {
|
KeyCode::Right => {
|
||||||
app.cmdline_position = std::cmp::min(
|
command_options.cmdline_position = std::cmp::min(
|
||||||
app.cmdline.len() as u16,
|
command_options.cmdline.len() as u16,
|
||||||
app.cmdline_position.saturating_add(1),
|
command_options.cmdline_position.saturating_add(1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
EventMessage::CrosstermEvent(Event::Paste(data)) => {
|
Event::Paste(data) => {
|
||||||
app.cmdline.insert_str(app.cmdline_position as usize, &data);
|
let cmdline_position = command_options.cmdline_position as usize;
|
||||||
app.cmdline_position = app.cmdline_position.saturating_add(data.len() as u16);
|
command_options.cmdline.insert_str(cmdline_position, &data);
|
||||||
|
command_options.cmdline_position = command_options
|
||||||
|
.cmdline_position
|
||||||
|
.saturating_add(data.len() as u16);
|
||||||
|
|
||||||
if app.autorun {
|
if command_options.autorun {
|
||||||
command::run(&mut app);
|
app.run_command()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventMessage::CrosstermEvent(Event::FocusGained)
|
Event::FocusGained
|
||||||
| EventMessage::CrosstermEvent(Event::FocusLost)
|
| Event::FocusLost
|
||||||
| EventMessage::CrosstermEvent(Event::Mouse(_))
|
| Event::Mouse(_)
|
||||||
| EventMessage::CrosstermEvent(Event::Resize(_, _)) => {}
|
| Event::Resize(_, _) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventMessage::CommandCompleted(command_completed) => {
|
||||||
|
let mut command_options = app.command_options.write();
|
||||||
|
match command_completed {
|
||||||
|
command::CommandCompleted::Success(c) => {
|
||||||
|
command_options.command_result = c;
|
||||||
|
}
|
||||||
|
command::CommandCompleted::Failure(stderr) => {
|
||||||
|
command_options.command_result.status_success = false;
|
||||||
|
command_options.command_result.stderr = stderr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
_ => {}
|
_ => {
|
||||||
|
bail!("Unknown event type happened");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
src/ui.rs
24
src/ui.rs
@ -30,6 +30,8 @@ use crossterm::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
|
pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
|
||||||
|
let command_options = app.command_options.read();
|
||||||
|
|
||||||
let vertical_chunks = Layout::default()
|
let vertical_chunks = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(3), Constraint::Length(3)].as_ref())
|
.constraints([Constraint::Min(3), Constraint::Length(3)].as_ref())
|
||||||
@ -46,17 +48,17 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
|
|||||||
chunks[0],
|
chunks[0],
|
||||||
);
|
);
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(app.cmdline.as_ref()).block(
|
Paragraph::new(command_options.cmdline.as_ref()).block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.title(format!(
|
.title(format!(
|
||||||
"Cmdline ({}{}{})",
|
"Cmdline ({}{}{})",
|
||||||
app.command,
|
command_options.command,
|
||||||
if app.hidden_options.is_empty() {
|
if command_options.hidden_options.is_empty() {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
" "
|
" "
|
||||||
},
|
},
|
||||||
app.hidden_options.join(" ")
|
command_options.hidden_options.join(" ")
|
||||||
))
|
))
|
||||||
.borders(Borders::ALL),
|
.borders(Borders::ALL),
|
||||||
),
|
),
|
||||||
@ -64,18 +66,18 @@ pub fn draw<B: Backend>(f: &mut Frame<B>, app: &App) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Render the output in the outpout region
|
// Render the output in the outpout region
|
||||||
ui_output(f, chunks[1], app);
|
ui_output(f, chunks[1], &command_options.command_result);
|
||||||
|
|
||||||
f.set_cursor(
|
f.set_cursor(
|
||||||
vertical_chunks[1].x + app.cmdline_position + 1,
|
vertical_chunks[1].x + command_options.cmdline_position + 1,
|
||||||
vertical_chunks[1].y + 1,
|
vertical_chunks[1].y + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui_output<B: Backend>(f: &mut Frame<B>, output_region: Rect, app: &App) {
|
fn ui_output<B: Backend>(f: &mut Frame<B>, output_region: Rect, command_result: &CommandResult) {
|
||||||
if app.command_result.status_success {
|
if command_result.status_success {
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(bytes_to_text(&app.command_result.stdout))
|
Paragraph::new(bytes_to_text(&command_result.stdout))
|
||||||
.block(Block::default().title("New").borders(Borders::ALL)),
|
.block(Block::default().title("New").borders(Borders::ALL)),
|
||||||
output_region,
|
output_region,
|
||||||
);
|
);
|
||||||
@ -86,12 +88,12 @@ fn ui_output<B: Backend>(f: &mut Frame<B>, output_region: Rect, app: &App) {
|
|||||||
.split(output_region);
|
.split(output_region);
|
||||||
|
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(bytes_to_text(&app.command_result.stdout))
|
Paragraph::new(bytes_to_text(&command_result.stdout))
|
||||||
.block(Block::default().title("Output").borders(Borders::ALL)),
|
.block(Block::default().title("Output").borders(Borders::ALL)),
|
||||||
chunks[0],
|
chunks[0],
|
||||||
);
|
);
|
||||||
f.render_widget(
|
f.render_widget(
|
||||||
Paragraph::new(bytes_to_text(&app.command_result.stderr))
|
Paragraph::new(bytes_to_text(&command_result.stderr))
|
||||||
.block(Block::default().title("Error").borders(Borders::ALL)),
|
.block(Block::default().title("Error").borders(Borders::ALL)),
|
||||||
chunks[1],
|
chunks[1],
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user