diff options
author | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-02-24 13:45:36 -0500 |
---|---|---|
committer | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-02-24 14:05:16 -0500 |
commit | 72111d986965861fa694f8a111b712fdad76b6bc (patch) | |
tree | e26951aee33dccb5986f8861e4594231cfc9d8a4 /src | |
parent | d38dcdbbee44187bdb605dbf9bbf9c6c6d3e4f35 (diff) |
fix open_file_with
- fix event to not consume the first few inputs on switching to a terminal program
Diffstat (limited to 'src')
-rw-r--r-- | src/commands/mod.rs | 6 | ||||
-rw-r--r-- | src/commands/open_file.rs | 83 | ||||
-rw-r--r-- | src/commands/rename_file.rs | 4 | ||||
-rw-r--r-- | src/config/mimetype.rs | 76 | ||||
-rw-r--r-- | src/fs/dirlist.rs | 6 | ||||
-rw-r--r-- | src/run.rs | 3 | ||||
-rw-r--r-- | src/ui/tui_backend.rs | 24 | ||||
-rw-r--r-- | src/ui/widgets/tui_menu.rs | 7 | ||||
-rw-r--r-- | src/ui/widgets/tui_prompt.rs | 3 | ||||
-rw-r--r-- | src/ui/widgets/tui_textfield.rs | 49 | ||||
-rw-r--r-- | src/util/event.rs | 24 |
11 files changed, 153 insertions, 132 deletions
diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 724b5ad..6d31197 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -27,7 +27,7 @@ pub use self::cursor_move::{ pub use self::delete_files::DeleteFiles; pub use self::file_ops::{CopyFiles, CutFiles, PasteFiles}; pub use self::new_directory::NewDirectory; -pub use self::open_file::OpenFile; //, OpenFileWith}; +pub use self::open_file::{OpenFile, OpenFileWith}; pub use self::parent_directory::ParentDirectory; pub use self::quit::ForceQuit; pub use self::quit::Quit; @@ -156,9 +156,7 @@ pub fn from_args(command: String, args: Vec<String>) -> JoshutoResult<Box<dyn Jo "new_tab" => Ok(Box::new(self::NewTab::new())), "open_file" => Ok(Box::new(self::OpenFile::new())), - /* - "open_file_with" => Ok(Box::new(self::OpenFileWith::new())), - */ + "open_file_with" => Ok(Box::new(self::OpenFileWith::new())), "paste_files" => { let mut options = Options::default(); for arg in args { diff --git a/src/commands/open_file.rs b/src/commands/open_file.rs index f41ee7e..2a900e0 100644 --- a/src/commands/open_file.rs +++ b/src/commands/open_file.rs @@ -55,7 +55,9 @@ impl OpenFile { } else if let Some(paths) = filepaths { let options = Self::get_options(paths[0]); if options.len() > 0 { + backend.terminal_drop(); options[0].execute_with(&paths)?; + backend.terminal_restore(); } else { } } @@ -77,7 +79,7 @@ impl JoshutoRunnable for OpenFile { Ok(()) } } -/* + #[derive(Clone, Debug)] pub struct OpenFileWith; @@ -89,55 +91,43 @@ impl OpenFileWith { "open_file_with" } - pub fn open_with(paths: &[&PathBuf]) -> std::io::Result<()> { - const PROMPT: &str = ":open_with "; - + pub fn open_with(context: &JoshutoContext, backend: &mut TuiBackend, paths: &[&PathBuf]) -> std::io::Result<()> { let mimetype_options: Vec<&JoshutoMimetypeEntry> = OpenFile::get_options(&paths[0]); - let user_input: Option<String> = { - let (term_rows, term_cols) = ui::getmaxyx(); - - let option_size = mimetype_options.len(); - let display_win = window::JoshutoPanel::new( - option_size as i32 + 2, - term_cols, - (term_rows as usize - option_size - 2, 0), - ); - - let mut display_vec: Vec<String> = Vec::with_capacity(option_size); - for (i, val) in mimetype_options.iter().enumerate() { - display_vec.push(format!(" {}\t{}", i, val)); - } - display_vec.sort(); - display_win.move_to_top(); - ui::display_menu(&display_win, &display_vec); - ncurses::doupdate(); - - let textfield = - JoshutoTextField::new(1, term_cols, (term_rows as usize - 1, 0), PROMPT, "", ""); - textfield.readline() - }; - ncurses::doupdate(); + let mut textfield = TuiTextField::default() + .prompt(":") + .prefix("open_with "); + let user_input: Option<String> = textfield.get_input(backend, &context); match user_input.as_ref() { None => Ok(()), - Some(user_input) if user_input.is_empty() => Ok(()), Some(user_input) => match user_input.parse::<usize>() { Ok(n) if n >= mimetype_options.len() => Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "option does not exist".to_owned(), )), - Ok(n) => mimetype_options[n].execute_with(paths), + Ok(n) => { + backend.terminal_drop(); + let res = mimetype_options[n].execute_with(paths); + backend.terminal_restore()?; + res + } Err(_) => { let mut args_iter = user_input.split_whitespace(); + args_iter.next(); match args_iter.next() { - Some(cmd) => JoshutoMimetypeEntry::new(String::from(cmd)) - .add_args(args_iter) - .execute_with(paths), + Some(cmd) => { + backend.terminal_drop(); + let res = JoshutoMimetypeEntry::new(String::from(cmd)) + .args(args_iter) + .execute_with(paths); + backend.terminal_restore()?; + res + } None => Ok(()), } } - }, + } } } } @@ -151,20 +141,21 @@ impl std::fmt::Display for OpenFileWith { } impl JoshutoRunnable for OpenFileWith { - fn execute(&self, context: &mut JoshutoContext, _: &mut TuiBackend) -> JoshutoResult<()> { - let curr_list = &context.tabs[context.curr_tab_index].curr_list; - match curr_list.index { - None => { - return Err(JoshutoError::new( - JoshutoErrorKind::IONotFound, - String::from("No files selected"), - )) + fn execute(&self, context: &mut JoshutoContext, backend: &mut TuiBackend) -> JoshutoResult<()> { + let paths = match &context.tabs[context.curr_tab_index].curr_list_ref() { + Some(curr_list) => { + curr_list.get_selected_paths() } - Some(_) => {} + None => vec![], + }; + + if paths.is_empty() { + return Err(JoshutoError::new( + JoshutoErrorKind::IONotFound, + String::from("No files selected"), + )) } - let paths = curr_list.get_selected_paths(); - Self::open_with(&paths)?; + Self::open_with(context, backend, &paths)?; Ok(()) } } -*/ diff --git a/src/commands/rename_file.rs b/src/commands/rename_file.rs index 763a638..8d666d3 100644 --- a/src/commands/rename_file.rs +++ b/src/commands/rename_file.rs @@ -79,7 +79,7 @@ impl RenameFileAppend { &self, context: &mut JoshutoContext, backend: &mut TuiBackend, - file_name: String, + file_name: &str, ) -> JoshutoResult<()> { let prefix; let suffix; @@ -115,7 +115,7 @@ impl JoshutoRunnable for RenameFileAppend { } if let Some(file_name) = file_name { - self.rename_file(context, backend, file_name)?; + self.rename_file(context, backend, file_name.as_str())?; } Ok(()) } diff --git a/src/config/mimetype.rs b/src/config/mimetype.rs index 507b165..a4d6c44 100644 --- a/src/config/mimetype.rs +++ b/src/config/mimetype.rs @@ -14,78 +14,76 @@ const fn default_false() -> bool { #[derive(Debug, Deserialize)] pub struct JoshutoMimetypeEntry { - command: String, - #[serde(default)] - args: Vec<String>, - #[serde(default = "default_false")] - fork: bool, - #[serde(default = "default_false")] - silent: bool, - #[serde(default = "default_false")] - confirm_exit: bool, + #[serde(rename = "command")] + _command: String, + #[serde(default, rename = "args")] + _args: Vec<String>, + #[serde(default, rename = "fork")] + _fork: bool, + #[serde(default, rename = "silent")] + _silent: bool, + #[serde(default, rename = "confirm_exit")] + _confirm_exit: bool, } -#[allow(dead_code)] impl JoshutoMimetypeEntry { pub fn new(command: String) -> Self { Self { - command, - args: Vec::new(), - fork: false, - silent: false, - confirm_exit: false, + _command: command, + _args: Vec::new(), + _fork: false, + _silent: false, + _confirm_exit: false, } } - pub fn add_arg<S: std::convert::Into<String>>(&mut self, arg: S) -> &mut Self { - self.args.push(arg.into()); + pub fn arg<S: std::convert::Into<String>>(mut self, arg: S) -> Self { + self._args.push(arg.into()); self } - pub fn add_args<I, S>(&mut self, args: I) -> &mut Self + pub fn args<I, S>(mut self, args: I) -> Self where - I: IntoIterator<Item = S>, + I: Iterator<Item = S>, S: std::convert::Into<String>, { - for arg in args { - self.args.push(arg.into()); - } + args.for_each(|arg| self._args.push(arg.into())); self } - pub fn set_fork(&mut self, set: bool) -> &mut Self { - self.fork = set; + pub fn fork(mut self, fork: bool) -> Self { + self._fork = fork; self } - pub fn set_silent(&mut self, set: bool) -> &mut Self { - self.silent = set; + pub fn silent(mut self, silent: bool) -> Self { + self._silent = silent; self } - pub fn set_confirm_exit(&mut self, set: bool) -> &mut Self { - self.confirm_exit = set; + pub fn set_confirm_exit(mut self, confirm_exit: bool) -> Self { + self._confirm_exit = confirm_exit; self } pub fn get_command(&self) -> &str { - &self.command + self._command.as_str() } pub fn get_args(&self) -> &[String] { - &self.args + &self._args } pub fn get_fork(&self) -> bool { - self.fork + self._fork } pub fn get_silent(&self) -> bool { - self.silent + self._silent } pub fn get_confirm_exit(&self) -> bool { - self.confirm_exit + self._confirm_exit } pub fn execute_with(&self, paths: &[&PathBuf]) -> std::io::Result<()> { @@ -112,6 +110,18 @@ impl JoshutoMimetypeEntry { } } +impl std::default::Default for JoshutoMimetypeEntry { + fn default() -> Self { + Self { + _command: "".to_string(), + _args: Vec::new(), + _fork: false, + _silent: false, + _confirm_exit: false, + } + } +} + impl std::fmt::Display for JoshutoMimetypeEntry { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.get_command()).unwrap(); diff --git a/src/fs/dirlist.rs b/src/fs/dirlist.rs index cf53457..c099442 100644 --- a/src/fs/dirlist.rs +++ b/src/fs/dirlist.rs @@ -92,13 +92,13 @@ impl JoshutoDirList { pub fn get_selected_paths(&self) -> Vec<&path::PathBuf> { let vec: Vec<&path::PathBuf> = self.selected_entries().map(|e| e.file_path()).collect(); - if vec.is_empty() { + if !vec.is_empty() { + vec + } else { match self.get_curr_ref() { Some(s) => vec![s.file_path()], _ => vec![], } - } else { - vec } } @@ -36,16 +36,13 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: let worker = context.worker_queue.pop_front().unwrap(); io_handle = { let event_tx = context.events.event_tx.clone(); - let sync_tx = context.events.sync_tx.clone(); let thread = thread::spawn(move || { worker.start(); while let Ok(evt) = worker.recv() { let _ = event_tx.send(evt); - let _ = sync_tx.send(()); } worker.handle.join(); let _ = event_tx.send(Event::IOWorkerResult); - let _ = sync_tx.send(()); }); Some(thread) }; diff --git a/src/ui/tui_backend.rs b/src/ui/tui_backend.rs index 446d1c0..498e3f6 100644 --- a/src/ui/tui_backend.rs +++ b/src/ui/tui_backend.rs @@ -1,10 +1,13 @@ +use std::io::Write; +use std::io::stdout; + use termion::raw::{IntoRawMode, RawTerminal}; use termion::screen::AlternateScreen; use tui::backend::TermionBackend; use tui::widgets::Widget; pub struct TuiBackend { - pub terminal: tui::Terminal<TermionBackend<AlternateScreen<RawTerminal<std::io::Stdout>>>>, + pub terminal: Option<tui::Terminal<TermionBackend<AlternateScreen<RawTerminal<std::io::Stdout>>>>>, } impl TuiBackend { @@ -14,16 +17,31 @@ impl TuiBackend { let backend = TermionBackend::new(stdout); let mut terminal = tui::Terminal::new(backend)?; terminal.hide_cursor()?; - Ok(Self { terminal }) + Ok(Self { terminal: Some(terminal) }) } pub fn render<W>(&mut self, widget: &mut W) where W: Widget, { - self.terminal.draw(|mut frame| { + self.terminal_mut().draw(|mut frame| { let rect = frame.size(); widget.render(&mut frame, rect); }); } + + pub fn terminal_mut(&mut self) -> &mut tui::Terminal<TermionBackend<AlternateScreen<RawTerminal<std::io::Stdout>>>> { + self.terminal.as_mut().unwrap() + } + + pub fn terminal_drop(&mut self) { + let _ = self.terminal.take(); + stdout().flush(); + } + + pub fn terminal_restore(&mut self) -> std::io::Result<()> { + let mut new_backend = TuiBackend::new()?; + std::mem::swap(&mut self.terminal, &mut new_backend.terminal); + Ok(()) + } } diff --git a/src/ui/widgets/tui_menu.rs b/src/ui/widgets/tui_menu.rs index 9dde1fe..fdd291e 100644 --- a/src/ui/widgets/tui_menu.rs +++ b/src/ui/widgets/tui_menu.rs @@ -31,9 +31,10 @@ impl TuiCommandMenu { m: &'a JoshutoCommandMapping, ) -> Option<&'a Box<dyn JoshutoCommand>> { let mut map: &JoshutoCommandMapping = &m; + let mut terminal = backend.terminal_mut(); loop { - backend.terminal.draw(|mut frame| { + terminal.draw(|mut frame| { let f_size = frame.size(); { @@ -101,6 +102,10 @@ impl<'a> TuiMenu<'a> { pub fn new(options: &'a Vec<&str>) -> Self { Self { options } } + + pub fn len(&self) -> usize { + self.options.len() + } } const LONG_SPACE: &str = " "; diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs index 21526f3..c01965a 100644 --- a/src/ui/widgets/tui_prompt.rs +++ b/src/ui/widgets/tui_prompt.rs @@ -20,8 +20,9 @@ impl<'a> TuiPrompt<'a> { } pub fn get_key(&mut self, backend: &mut TuiBackend, context: &JoshutoContext) -> Key { + let mut terminal = backend.terminal_mut(); loop { - backend.terminal.draw(|mut frame| { + terminal.draw(|mut frame| { let f_size = frame.size(); if f_size.height == 0 { return; diff --git a/src/ui/widgets/tui_textfield.rs b/src/ui/widgets/tui_textfield.rs index 9dbbad3..6d89b14 100644 --- a/src/ui/widgets/tui_textfield.rs +++ b/src/ui/widgets/tui_textfield.rs @@ -9,7 +9,7 @@ use tui::backend::Backend; use tui::layout::Rect; use tui::style::{Color, Style}; use tui::widgets::{Block, Borders, List, Paragraph, Text, Widget}; -use unicode_width::UnicodeWidthStr; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use crate::context::JoshutoContext; use crate::ui::TuiBackend; @@ -77,25 +77,25 @@ impl<'a> TuiTextField<'a> { ._prefix .char_indices() .last() - .map(|(i, c)| i) + .map(|(i, c)| i + c.width().unwrap_or(0)) .unwrap_or(0); line_buffer.insert_str(0, self._prefix); - line_buffer.insert_str(line_buffer.len(), self._suffix); + line_buffer.insert_str(char_idx, self._suffix); line_buffer.set_pos(char_idx); - backend.terminal.show_cursor(); - let mut cursor_xpos = line_buffer.pos() + 1; + let mut terminal = backend.terminal_mut();; + terminal.show_cursor(); + let mut cursor_xpos = self._prefix.len() + 1; { - let frame = backend.terminal.get_frame(); + let frame = terminal.get_frame(); let f_size = frame.size(); - backend - .terminal + terminal .set_cursor(cursor_xpos as u16, f_size.height - 1); } loop { - backend.terminal.draw(|mut frame| { + terminal.draw(|mut frame| { let f_size = frame.size(); if f_size.height == 0 { return; @@ -107,15 +107,21 @@ impl<'a> TuiTextField<'a> { view.render(&mut frame, f_size); } - let top_rect = Rect { - x: 0, - y: 0, - width: f_size.width, - height: 1, - }; - if let Some(menu) = self._menu.as_mut() { - menu.render(&mut frame, top_rect); + let menu_len = menu.len(); + let menu_y = if menu_len + 1 > f_size.height as usize { + 0 + } else { + (f_size.height as usize - menu_len - 1) as u16 + }; + + let rect = Rect { + x: 0, + y: menu_y, + width: f_size.width, + height: menu_len as u16, + }; + menu.render(&mut frame, rect); } let cmd_prompt_style = Style::default().fg(Color::LightGreen); @@ -170,7 +176,7 @@ impl<'a> TuiTextField<'a> { Event::Input(Key::Up) => {} Event::Input(Key::Down) => {} Event::Input(Key::Esc) => { - backend.terminal.hide_cursor(); + terminal.hide_cursor(); return None; } Event::Input(Key::Char('\t')) => { @@ -213,14 +219,13 @@ impl<'a> TuiTextField<'a> { } cursor_xpos = line_buffer.pos() + 1; { - let frame = backend.terminal.get_frame(); + let frame = terminal.get_frame(); let f_size = frame.size(); - backend - .terminal + terminal .set_cursor(cursor_xpos as u16, f_size.height - 1); } } - backend.terminal.hide_cursor(); + terminal.hide_cursor(); if line_buffer.as_str().is_empty() { None } else { diff --git a/src/util/event.rs b/src/util/event.rs index c6f8b85..5a76702 100644 --- a/src/util/event.rs +++ b/src/util/event.rs @@ -28,7 +28,6 @@ pub struct Events { pub event_tx: mpsc::Sender<Event>, event_rx: mpsc::Receiver<Event>, pub sync_tx: mpsc::SyncSender<()>, - sync_rx: mpsc::Receiver<()>, // fileio_handle: thread::JoinHandle<()>, } @@ -46,23 +45,21 @@ impl Events { let (event_tx, event_rx) = mpsc::channel(); { - let sync_tx = sync_tx.clone(); let event_tx = event_tx.clone(); thread::spawn(move || { let stdin = io::stdin(); let mut keys = stdin.keys(); - while let Some(evt) = keys.next() { - match evt { - Ok(key) => { - if let Err(e) = event_tx.send(Event::Input(key)) { - eprintln!("[{}] Input thread send err: {:#?}", prefix, e); - return; - } - if let Err(_) = sync_tx.send(()) { - return; + while let Ok(_) = sync_rx.recv() { + if let Some(evt) = keys.next() { + match evt { + Ok(key) => { + if let Err(e) = event_tx.send(Event::Input(key)) { + eprintln!("[{}] Input thread send err: {:#?}", prefix, e); + return; + } } + _ => {} } - _ => {} } } }) @@ -72,14 +69,13 @@ impl Events { event_tx, event_rx, sync_tx, - sync_rx, prefix, } } pub fn next(&self) -> Result<Event, mpsc::RecvError> { + self.sync_tx.try_send(()); let event = self.event_rx.recv()?; - self.sync_rx.recv()?; Ok(event) } /* |