diff --git a/build.rs b/build.rs index a69d83a..a6b5311 100644 --- a/build.rs +++ b/build.rs @@ -1,26 +1,8 @@ -extern crate termion; extern crate rustc_version; - -use rustc_version::{version_meta, Channel}; - +extern crate termion; // use std::process::Command; - -fn main() -> Result<(),()> { - // Bail out if compiler isn't a nightly - if let Ok(false) = version_meta().map(|m| m.channel == Channel::Nightly) { - eprint!("{}", termion::color::Fg(termion::color::Red)); - eprint!("{}", termion::style::Bold); - eprint!("{}", termion::style::Underline); - eprintln!("NIHGTLY COMPILER required"); - eprintln!("Please install a nighlty compiler to proceed: https://rustup.rs/"); - eprint!("{}", termion::style::Reset); - eprintln!("rustup toolchain install nightly"); - eprintln!("source ~/.cargo/env"); - - return Err(()); - } - +fn main() -> Result<(), ()> { // crates.io doesn't allow question marks in file names // So we just stuff that in an archive for distribution diff --git a/src/bookmarks.rs b/src/bookmarks.rs index 208ace0..e59a382 100644 --- a/src/bookmarks.rs +++ b/src/bookmarks.rs @@ -2,10 +2,10 @@ use termion::event::Key; use std::collections::HashMap; -use crate::fail::{HResult, HError, ErrorLog}; -use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; +use crate::fail::{ErrorLog, HError, HResult}; use crate::term; +use crate::widget::{Widget, WidgetCore}; #[derive(PartialEq, Eq, Clone, Debug)] pub struct Bookmarks { @@ -14,8 +14,12 @@ pub struct Bookmarks { impl Bookmarks { pub fn new() -> Bookmarks { - let mut bm = Bookmarks { mapping: HashMap::new() }; - bm.load().or_else(|_| HError::log("Couldn't load bookmarks!")).ok(); + let mut bm = Bookmarks { + mapping: HashMap::new(), + }; + bm.load() + .or_else(|_| HError::log("Couldn't load bookmarks!")) + .ok(); bm } pub fn add(&mut self, key: char, path: &str) -> HResult<()> { @@ -24,9 +28,10 @@ impl Bookmarks { Ok(()) } pub fn get(&self, key: char) -> HResult<&String> { - let path = self.mapping.get(&key)?; + let path = self.mapping.get(&key).ok_or_else(|| HError::NoneError)?; Ok(path) } + pub fn load(&mut self) -> HResult<()> { let bm_file = crate::paths::bookmark_path()?; @@ -35,8 +40,7 @@ impl Bookmarks { } let bm_content = std::fs::read_to_string(bm_file)?; - let mapping = bm_content.lines() - .fold(HashMap::new(), |mut bm, line| { + let mapping = bm_content.lines().fold(HashMap::new(), |mut bm, line| { let parts = line.splitn(2, ":").collect::>(); if parts.len() == 2 { if let Some(key) = parts[0].chars().next() { @@ -62,9 +66,11 @@ impl Bookmarks { } pub fn save(&self) -> HResult<()> { let bm_file = crate::paths::bookmark_path()?; - let bookmarks = self.mapping.iter().map(|(key, path)| { - format!("{}:{}\n", key, path) - }).collect::(); + let bookmarks = self + .mapping + .iter() + .map(|(key, path)| format!("{}:{}\n", key, path)) + .collect::(); std::fs::write(bm_file, bookmarks)?; @@ -72,7 +78,6 @@ impl Bookmarks { } } - pub struct BMPopup { core: WidgetCore, bookmarks: Bookmarks, @@ -86,7 +91,7 @@ impl BMPopup { core: core.clone(), bookmarks: Bookmarks::new(), bookmark_path: None, - add_mode: false + add_mode: false, }; bmpopup.set_coordinates(&core.coordinates).log(); bmpopup @@ -96,8 +101,8 @@ impl BMPopup { self.bookmark_path = Some(cwd); self.refresh()?; match self.popup() { - Ok(_) => {}, - Err(HError::PopupFinnished) => {}, + Ok(_) => {} + Err(HError::PopupFinnished) => {} err @ Err(HError::TerminalResizedError) => err?, err @ Err(HError::WidgetResizedError) => err?, err @ Err(_) => err?, @@ -105,7 +110,7 @@ impl BMPopup { self.get_core()?.clear()?; let bookmark = self.bookmark_path.take(); - Ok(bookmark?) + Ok(bookmark.ok_or_else(|| HError::NoneError)?) } pub fn add(&mut self, path: &str) -> HResult<()> { @@ -132,11 +137,11 @@ impl BMPopup { crate::term::reset(), key, path, - padding = padding as usize) + padding = padding as usize + ) } } - impl Widget for BMPopup { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) @@ -155,9 +160,11 @@ impl Widget for BMPopup { fn set_coordinates(&mut self, _: &Coordinates) -> HResult<()> { let (xsize, ysize) = crate::term::size()?; let len = self.bookmarks.mapping.len(); - let ysize = ysize.saturating_sub( len + 1 ); + let ysize = ysize.saturating_sub(len + 1); - self.core.coordinates.set_size_u(xsize.saturating_sub(1), len); + self.core + .coordinates + .set_size_u(xsize.saturating_sub(1), len); self.core.coordinates.set_position_u(1, ysize); Ok(()) @@ -169,14 +176,23 @@ impl Widget for BMPopup { let mut drawlist = String::new(); if !self.add_mode { - let cwd = self.bookmark_path.as_ref()?; + let cwd = self + .bookmark_path + .as_ref() + .ok_or_else(|| HError::NoneError)?; drawlist += &self.render_line(ypos, &'`', cwd); } - let bm_list = self.bookmarks.mapping.iter().enumerate().map(|(i, (key, path))| { - let line = i as u16 + ypos + 1; - self.render_line(line, key, path) - }).collect::(); + let bm_list = self + .bookmarks + .mapping + .iter() + .enumerate() + .map(|(i, (key, path))| { + let line = i as u16 + ypos + 1; + self.render_line(line, key, path) + }) + .collect::(); drawlist += &bm_list; @@ -186,12 +202,12 @@ impl Widget for BMPopup { match key { Key::Ctrl('c') | Key::Esc => { self.bookmark_path = None; - return HError::popup_finnished() - }, + return HError::popup_finnished(); + } Key::Char('`') => return HError::popup_finnished(), Key::Char(key) => { if self.add_mode { - let path = self.bookmark_path.take()?; + let path = self.bookmark_path.take().ok_or_else(|| HError::NoneError)?; self.bookmarks.add(key, &path)?; self.add_mode = false; self.bookmarks.save().log(); diff --git a/src/config.rs b/src/config.rs index c4e7049..2e761d6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,14 +1,13 @@ -use lazy_static; use clap; +use lazy_static; use std::sync::RwLock; use crate::paths; -use crate::fail::{HError, HResult, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult}; use crate::keybind::KeyBinds; - #[derive(Clone)] // These are options, so we know if they have been set or not struct ArgvConfig { @@ -24,16 +23,15 @@ impl ArgvConfig { animation: None, show_hidden: None, icons: None, - graphics: None + graphics: None, } } } lazy_static! { - static ref ARGV_CONFIG: RwLock = RwLock::new(ArgvConfig::new()); + static ref ARGV_CONFIG: RwLock = RwLock::new(ArgvConfig::new()); } - pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> { let animation = args.is_present("animation-off"); let show_hidden = args.is_present("show-hidden"); @@ -66,7 +64,7 @@ pub fn set_argv_config(args: clap::ArgMatches) -> HResult<()> { } fn get_argv_config() -> HResult { - Ok(ARGV_CONFIG.try_read()?.clone()) + Ok(ARGV_CONFIG.try_read()?.clone()) } fn infuse_argv_config(mut config: Config) -> Config { @@ -93,12 +91,11 @@ pub struct Config { pub media_mute: bool, pub media_previewer: String, pub media_previewer_exists: bool, - pub ratios: Vec::, + pub ratios: Vec, pub graphics: String, pub keybinds: KeyBinds, } - impl Config { pub fn new() -> Config { let config = Config::default(); @@ -119,7 +116,7 @@ impl Config { media_mute: false, media_previewer: "hunter-media".to_string(), media_previewer_exists: false, - ratios: vec![20,30,49], + ratios: vec![20, 30, 49], graphics: detect_g_mode(), keybinds: KeyBinds::default(), } @@ -134,80 +131,84 @@ impl Config { let config_string = std::fs::read_to_string(config_path)?; - let config = config_string.lines().fold(Config::new(), |mut config, line| { - match Config::prep_line(line) { - Ok(("animation", "on")) => config.animation = true, - Ok(("animation", "off")) => config.animation = false, - Ok(("animation_refresh_frequency", frequency)) => { - match frequency.parse::() { - Ok(parsed_freq) => config.animation_refresh_frequency = parsed_freq, - _ => HError::config_error::(line.to_string()).log() + let config = config_string + .lines() + .fold(Config::new(), |mut config, line| { + match Config::prep_line(line) { + Ok(("animation", "on")) => config.animation = true, + Ok(("animation", "off")) => config.animation = false, + Ok(("animation_refresh_frequency", frequency)) => { + match frequency.parse::() { + Ok(parsed_freq) => config.animation_refresh_frequency = parsed_freq, + _ => HError::config_error::(line.to_string()).log(), + } } - } - Ok(("show_hidden", "on")) => config.show_hidden = true, - Ok(("show_hidden", "off")) => config.show_hidden = false, - Ok(("icons", "on")) => config.icons = true, - Ok(("icons", "off")) => config.icons = false, - Ok(("icons_space", "on")) => config.icons_space = true, - Ok(("icons_space", "off")) => config.icons_space = false, - Ok(("select_cmd", cmd)) => { - let cmd = cmd.to_string(); - config.select_cmd = cmd; - } - Ok(("cd_cmd", cmd)) => { - let cmd = cmd.to_string(); - config.cd_cmd = cmd; - } - Ok(("media_autoplay", "on")) => config.media_autoplay = true, - Ok(("media_autoplay", "off")) => config.media_autoplay = false, - Ok(("media_mute", "on")) => config.media_mute = true, - Ok(("media_mute", "off")) => config.media_mute = false, - Ok(("media_previewer", cmd)) => { - let cmd = cmd.to_string(); - config.media_previewer = cmd; - }, - Ok(("ratios", ratios)) => { - let ratios_str = ratios.to_string(); - if ratios_str.chars().all(|x| x.is_digit(10) || x.is_whitespace() - || x == ':' || x == ',' ) { - let ratios: Vec = ratios_str.split([',', ':'].as_ref()) - .map(|r| r.trim() - .parse().unwrap()) - .collect(); - let ratios_sum: usize = ratios.iter().sum(); - if ratios.len() == 3 && ratios_sum > 0 && - ratios - .iter() - .filter(|&r| *r > u16::max_value() as usize) - .next() == None { + Ok(("show_hidden", "on")) => config.show_hidden = true, + Ok(("show_hidden", "off")) => config.show_hidden = false, + Ok(("icons", "on")) => config.icons = true, + Ok(("icons", "off")) => config.icons = false, + Ok(("icons_space", "on")) => config.icons_space = true, + Ok(("icons_space", "off")) => config.icons_space = false, + Ok(("select_cmd", cmd)) => { + let cmd = cmd.to_string(); + config.select_cmd = cmd; + } + Ok(("cd_cmd", cmd)) => { + let cmd = cmd.to_string(); + config.cd_cmd = cmd; + } + Ok(("media_autoplay", "on")) => config.media_autoplay = true, + Ok(("media_autoplay", "off")) => config.media_autoplay = false, + Ok(("media_mute", "on")) => config.media_mute = true, + Ok(("media_mute", "off")) => config.media_mute = false, + Ok(("media_previewer", cmd)) => { + let cmd = cmd.to_string(); + config.media_previewer = cmd; + } + Ok(("ratios", ratios)) => { + let ratios_str = ratios.to_string(); + if ratios_str + .chars() + .all(|x| x.is_digit(10) || x.is_whitespace() || x == ':' || x == ',') + { + let ratios: Vec = ratios_str + .split([',', ':'].as_ref()) + .map(|r| r.trim().parse().unwrap()) + .collect(); + let ratios_sum: usize = ratios.iter().sum(); + if ratios.len() == 3 + && ratios_sum > 0 + && ratios + .iter() + .filter(|&r| *r > u16::max_value() as usize) + .next() + == None + { config.ratios = ratios; } + } } + #[cfg(feature = "sixel")] + Ok(("graphics", "sixel")) => config.graphics = "sixel".to_string(), + Ok(("graphics", "kitty")) => config.graphics = "kitty".to_string(), + Ok(("graphics", "auto")) => config.graphics = detect_g_mode(), + _ => { + HError::config_error::(line.to_string()).log(); + } + } + + #[cfg(feature = "img")] + match has_media_previewer(&config.media_previewer) { + t @ _ => config.media_previewer_exists = t, } - #[cfg(feature = "sixel")] - Ok(("graphics", - "sixel")) => config.graphics = "sixel".to_string(), - Ok(("graphics", - "kitty")) => config.graphics = "kitty".to_string(), - Ok(("graphics", - "auto")) => config.graphics = detect_g_mode(), - _ => { HError::config_error::(line.to_string()).log(); } - } - - #[cfg(feature = "img")] - match has_media_previewer(&config.media_previewer) { - t @ _ => config.media_previewer_exists = t - } - - config - }); + + config + }); let mut config = infuse_argv_config(config); //use std::iter::Extend; - KeyBinds::load() - .map(|kb| config.keybinds = kb) - .log(); + KeyBinds::load().map(|kb| config.keybinds = kb).log(); Ok(config) } @@ -219,7 +220,6 @@ impl Config { } else { HError::config_error(line.to_string()) } - } pub fn animate(&self) -> bool { @@ -241,8 +241,9 @@ fn detect_g_mode() -> String { "xterm-kitty" => "kitty", #[cfg(feature = "sixel")] "xterm" => "sixel", - _ => "unicode" - }.to_string() + _ => "unicode", + } + .to_string() } fn has_media_previewer(name: &str) -> bool { @@ -250,6 +251,6 @@ fn has_media_previewer(name: &str) -> bool { let previewer = std::path::Path::new(name); match previewer.is_absolute() { true => previewer.exists(), - false => find_bins(name).is_ok() + false => find_bins(name).is_ok(), } } diff --git a/src/config_installer.rs b/src/config_installer.rs index 4a2ca76..555537e 100644 --- a/src/config_installer.rs +++ b/src/config_installer.rs @@ -1,13 +1,12 @@ +use std::ffi::OsStr; use std::fs::*; use std::io::Write; -use std::process::Command; -use std::ffi::OsStr; use std::path::Path; +use std::process::Command; -use crate::fail::{HError, HResult, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult}; use crate::widget::WidgetCore; - pub fn ensure_config(core: WidgetCore) -> HResult<()> { if has_config()? { let previewers_path = crate::paths::previewers_path()?; @@ -16,32 +15,33 @@ pub fn ensure_config(core: WidgetCore) -> HResult<()> { if !previewers_path.exists() { core.show_status("Coulnd't find previewers in config dir! Adding!")?; install_config_previewers() - .or_else(|_| - core.show_status("Error installing previewers! Check log!"))?; + .or_else(|_| core.show_status("Error installing previewers! Check log!"))?; } if !actions_path.exists() { core.show_status("Coulnd't find actions in config dir! Adding!")?; install_config_actions() - .or_else(|_| - core.show_status("Error installing actions! Check log!"))?; + .or_else(|_| core.show_status("Error installing actions! Check log!"))?; } return Ok(()); } let msg = match install_config_all() { - Ok(_) => format!("Config installed in: {}", - crate::paths::hunter_path()?.to_string_lossy()), - Err(_) => format!("{}Problems with installation of default configuration! Look inside log.", - crate::term::color_red()), + Ok(_) => format!( + "Config installed in: {}", + crate::paths::hunter_path()?.to_string_lossy() + ), + Err(_) => format!( + "{}Problems with installation of default configuration! Look inside log.", + crate::term::color_red() + ), }; core.show_status(&msg)?; Ok(()) } - fn default_config_archive() -> &'static [u8] { let default_config = include_bytes!("../config.tar.gz"); default_config @@ -57,17 +57,18 @@ fn has_config() -> HResult { } } - fn install_config_all() -> HResult<()> { let hunter_dir = crate::paths::hunter_path()?; - let config_dir = hunter_dir.parent()?; + let config_dir = hunter_dir.parent().ok_or_else(|| HError::NoneError)?; if !hunter_dir.exists() { // create if non-existing - std::fs::create_dir(&hunter_dir) - .or_else(|_| HError::log(&format!("Couldn't create directory: {}", - hunter_dir.as_os_str() - .to_string_lossy())))?; + std::fs::create_dir(&hunter_dir).or_else(|_| { + HError::log(&format!( + "Couldn't create directory: {}", + hunter_dir.as_os_str().to_string_lossy() + )) + })?; } let archive_path = create_archive()?; @@ -87,9 +88,11 @@ fn copy(from: &Path, to: &Path) -> HResult<()> { .map(|s| s.success()); if success.is_err() || !success.unwrap() { - HError::log(&format!("Couldn't copy {} to {} !", - from.to_string_lossy(), - to.to_string_lossy())) + HError::log(&format!( + "Couldn't copy {} to {} !", + from.to_string_lossy(), + to.to_string_lossy() + )) } else { Ok(()) } @@ -145,7 +148,7 @@ pub fn update_config(core: WidgetCore, force: bool) -> HResult<()> { if force { install_config_previewers().log(); install_config_actions().log(); - return Ok(()) + return Ok(()); } let archive_path = create_archive()?; @@ -156,7 +159,7 @@ pub fn update_config(core: WidgetCore, force: bool) -> HResult<()> { fn update_dir>(source: P, target: P) -> HResult<()> { for file in std::fs::read_dir(source)? { let file_path = file?.path(); - let file_name = file_path.file_name()?; + let file_name = file_path.file_name().ok_or_else(|| HError::NoneError)?; let target_path = target.as_ref().join(file_name); if file_path.is_dir() { @@ -164,8 +167,11 @@ fn update_dir>(source: P, target: P) -> HResult<()> { update_dir(&file_path, &target_path).log(); } else { if !target_path.exists() { - HError::log::<()>(&format!("Installing additional config file: {}", - file_path.to_string_lossy())).ok(); + HError::log::<()>(&format!( + "Installing additional config file: {}", + file_path.to_string_lossy() + )) + .ok(); copy(&file_path, &target_path).log(); } } @@ -174,44 +180,49 @@ fn update_dir>(source: P, target: P) -> HResult<()> { Ok(()) } - fn create_archive() -> HResult<&'static str> { let archive_path = "/tmp/hunter-config.tar.gz"; let def_config = default_config_archive(); File::create(archive_path) - .and_then(|mut f| { - f.write_all(def_config).map(|_| f) - }) + .and_then(|mut f| f.write_all(def_config).map(|_| f)) .and_then(|mut f| f.flush()) .or_else(|_| { - HError::log(&format!("Failed to write config archive to: {}", - archive_path)) + HError::log(&format!( + "Failed to write config archive to: {}", + archive_path + )) })?; Ok(archive_path) } - fn extract_archive(to: &Path, archive_path: &str) -> HResult<()> { let success = Command::new("tar") - .args(&[OsStr::new("-C"), - to.as_os_str(), - OsStr::new("-xf"), - OsStr::new(archive_path)]) + .args(&[ + OsStr::new("-C"), + to.as_os_str(), + OsStr::new("-xf"), + OsStr::new(archive_path), + ]) .status() .or_else(|_| HError::log(&format!("Couldn't run tar!"))) .map(|s| s.success())?; if !success { - HError::log(&format!("Extraction of archive failed! Archive: {}", - archive_path))? + HError::log(&format!( + "Extraction of archive failed! Archive: {}", + archive_path + ))? } Ok(()) } fn delete_archive(archive_path: &str) -> HResult<()> { - std::fs::remove_file(archive_path) - .or_else(|_| HError::log(&format!("Deletion of archive failed! Archive: {}", - archive_path))) + std::fs::remove_file(archive_path).or_else(|_| { + HError::log(&format!( + "Deletion of archive failed! Archive: {}", + archive_path + )) + }) } diff --git a/src/coordinates.rs b/src/coordinates.rs index ac969ea..ee73bde 100644 --- a/src/coordinates.rs +++ b/src/coordinates.rs @@ -19,10 +19,10 @@ impl Coordinates { } } - pub fn new_at(xsize: u16, ysize: u16, xpos: u16, ypos: u16 ) -> Coordinates { + pub fn new_at(xsize: u16, ysize: u16, xpos: u16, ypos: u16) -> Coordinates { Coordinates { size: Size((xsize, ysize)), - position: Position((xpos, ypos)) + position: Position((xpos, ypos)), } } @@ -35,7 +35,7 @@ impl Coordinates { } pub fn set_size_u(&mut self, x: usize, y: usize) { - self.size.0 = ((x+1) as u16, (y+1) as u16); + self.size.0 = ((x + 1) as u16, (y + 1) as u16); } pub fn set_xsize(&mut self, x: u16) { @@ -51,7 +51,7 @@ impl Coordinates { } pub fn set_position_u(&mut self, x: usize, y: usize) { - self.position.0 = ((x+1) as u16, (y+1) as u16); + self.position.0 = ((x + 1) as u16, (y + 1) as u16); } pub fn set_xpos(&mut self, x: u16) { @@ -96,7 +96,7 @@ impl Coordinates { pub fn position_u(&self) -> (usize, usize) { let (xpos, ypos) = self.u16position(); - ((xpos-1) as usize, (ypos-1) as usize) + ((xpos - 1) as usize, (ypos - 1) as usize) } pub fn size(&self) -> &Size { @@ -109,7 +109,7 @@ impl Coordinates { pub fn size_u(&self) -> (usize, usize) { let (xsize, ysize) = self.u16size(); - ((xsize-1) as usize, (ysize-1) as usize) + ((xsize - 1) as usize, (ysize - 1) as usize) } pub fn size_pixels(&self) -> HResult<(usize, usize)> { @@ -117,10 +117,9 @@ impl Coordinates { let (cols, rows) = crate::term::size()?; let (xpix, ypix) = crate::term::size_pixels()?; // Cell dimensions - let (xpix, ypix) = (xpix/cols, ypix/rows); + let (xpix, ypix) = (xpix / cols, ypix / rows); // Frame dimensions - let (xpix, ypix) = (xpix * (xsize + 1), - ypix * (ysize + 1)); + let (xpix, ypix) = (xpix * (xsize + 1), ypix * (ysize + 1)); Ok((xpix as usize, ypix as usize)) } @@ -138,7 +137,7 @@ impl Size { } pub fn size_u(&self) -> (usize, usize) { let (xsize, ysize) = self.0; - ((xsize-1) as usize, (ysize-1) as usize) + ((xsize - 1) as usize, (ysize - 1) as usize) } pub fn xsize(&self) -> u16 { (self.0).0 @@ -154,7 +153,7 @@ impl Position { } pub fn position_u(&self) -> (usize, usize) { let (xpos, ypos) = self.0; - ((xpos-1) as usize, (ypos-1) as usize) + ((xpos - 1) as usize, (ypos - 1) as usize) } pub fn x(&self) -> u16 { (self.0).0 diff --git a/src/dirty.rs b/src/dirty.rs index 14c6245..f0e2309 100644 --- a/src/dirty.rs +++ b/src/dirty.rs @@ -1,5 +1,5 @@ -use std::sync::{Arc, RwLock}; use std::hash::{Hash, Hasher}; +use std::sync::{Arc, RwLock}; #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct DirtyBit(bool); @@ -33,7 +33,6 @@ pub trait Dirtyable { fn set_clean(&mut self); } - impl DirtyBit { pub fn new() -> DirtyBit { DirtyBit(false) @@ -46,7 +45,6 @@ impl AsyncDirtyBit { } } - impl Dirtyable for DirtyBit { fn is_dirty(&self) -> bool { self.0 diff --git a/src/fail.rs b/src/fail.rs index 519de19..0ce9ad4 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -3,7 +3,6 @@ use failure::Fail; //use failure::Backtrace; //use async_value::AError; - use termion::event::Key; use std::path::PathBuf; @@ -14,8 +13,6 @@ use crate::mediaview::MediaError; pub type HResult = Result; - - #[derive(Fail, Debug, Clone)] pub enum HError { #[fail(display = "IO error: {} ", _0)] @@ -25,17 +22,23 @@ pub enum HError { #[fail(display = "Can't lock!")] TryLockError, #[fail(display = "Channel failed: {}", error)] - ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError}, + ChannelTryRecvError { + #[cause] + error: std::sync::mpsc::TryRecvError, + }, #[fail(display = "Channel failed: {}", error)] - ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError}, + ChannelRecvError { + #[cause] + error: std::sync::mpsc::RecvError, + }, #[fail(display = "Channel failed")] ChannelSendError, #[fail(display = "Timer ran out while waiting for message on channel!")] ChannelRecvTimeout(#[cause] std::sync::mpsc::RecvTimeoutError), #[fail(display = "Previewer failed on file: {}", file)] - PreviewFailed{file: String}, + PreviewFailed { file: String }, #[fail(display = "StalePreviewer for file: {}", file)] - StalePreviewError{file: String}, + StalePreviewError { file: String }, #[fail(display = "Accessed stale value")] StaleError, #[fail(display = "Failed: {}", _0)] @@ -47,7 +50,7 @@ pub enum HError { #[fail(display = "No widget found")] NoWidgetError, #[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)] - WrongDirectoryError{ path: PathBuf, dir: PathBuf}, + WrongDirectoryError { path: PathBuf, dir: PathBuf }, #[fail(display = "Widget finnished")] PopupFinnished, #[fail(display = "No completions found")] @@ -60,18 +63,24 @@ pub enum HError { NoHeaderError, #[fail(display = "You wanted this!")] Quit, - #[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)] - HBoxWrongRatioError{ wnum: usize, ratio: Vec }, + #[fail( + display = "HBox ratio mismatch: {} widgets, ratio is {:?}", + wnum, ratio + )] + HBoxWrongRatioError { wnum: usize, ratio: Vec }, #[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)] - WrongWidgetError{got: String, wanted: String}, + WrongWidgetError { got: String, wanted: String }, #[fail(display = "Strip Prefix Error: {}", error)] - StripPrefixError{#[cause] error: std::path::StripPrefixError}, + StripPrefixError { + #[cause] + error: std::path::StripPrefixError, + }, #[fail(display = "INofify failed: {}", _0)] INotifyError(String), #[fail(display = "Tags not loaded yet")] TagsNotLoadedYetError, #[fail(display = "Undefined key: {:?}", key)] - WidgetUndefinedKeyError{key: Key}, + WidgetUndefinedKeyError { key: Key }, #[fail(display = "Terminal has been resized!")] TerminalResizedError, #[fail(display = "Widget has been resized!")] @@ -118,15 +127,19 @@ impl HError { Err(HError::Quit) } pub fn wrong_ratio(wnum: usize, ratio: Vec) -> HResult { - Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio }) + Err(HError::HBoxWrongRatioError { + wnum: wnum, + ratio: ratio, + }) } pub fn no_widget() -> HResult { Err(HError::NoWidgetError) } pub fn wrong_widget(got: &str, wanted: &str) -> HResult { - Err(HError::WrongWidgetError{ got: got.to_string(), - wanted: wanted.to_string() }) - + Err(HError::WrongWidgetError { + got: got.to_string(), + wanted: wanted.to_string(), + }) } pub fn popup_finnished() -> HResult { Err(HError::PopupFinnished) @@ -138,14 +151,14 @@ impl HError { Err(HError::WidgetUndefinedKeyError { key: key }) } pub fn wrong_directory(path: PathBuf, dir: PathBuf) -> HResult { - Err(HError::WrongDirectoryError{ path: path, - dir: dir }) - + Err(HError::WrongDirectoryError { + path: path, + dir: dir, + }) } pub fn preview_failed(file: &crate::files::File) -> HResult { let name = file.name.clone(); - Err(HError::PreviewFailed{ file: name }) - + Err(HError::PreviewFailed { file: name }) } pub fn terminal_resized() -> HResult { @@ -175,17 +188,14 @@ impl HError { pub fn input_updated(input: String) -> HResult { Err(HError::MiniBufferInputUpdated(input)) } - - } #[derive(Fail, Debug, Clone)] pub enum ErrorCause { #[fail(display = "{}", _0)] - Str(String) + Str(String), } - lazy_static! { static ref LOG: Mutex> = Mutex::new(vec![]); } @@ -200,7 +210,10 @@ pub fn put_log>(log: L) -> HResult<()> { Ok(()) } -pub trait ErrorLog where Self: Sized { +pub trait ErrorLog +where + Self: Sized, +{ fn log(self); fn log_and(self) -> Self; } @@ -220,7 +233,6 @@ pub trait ErrorLog where Self: Sized { // } // } - // impl ErrorLog for Result { // fn log(self) { // if let Err(err) = self { @@ -237,7 +249,9 @@ pub trait ErrorLog where Self: Sized { // } impl ErrorLog for Result -where E: Into + Clone { +where + E: Into + Clone, +{ fn log(self) { if let Err(err) = self { let err: HError = err.into(); @@ -254,11 +268,12 @@ where E: Into + Clone { } impl ErrorLog for E -where E: Into + Clone { +where + E: Into + Clone, +{ fn log(self) { let err: HError = self.into(); put_log(&err).ok(); - } fn log_and(self) -> Self { let err: HError = self.clone().into(); @@ -267,9 +282,6 @@ where E: Into + Clone { } } - - - impl From for HError { fn from(error: std::io::Error) -> Self { let err = HError::IoError(format!("{}", error)); @@ -326,16 +338,16 @@ impl From> for HError { } } -impl From for HError { - fn from(_error: std::option::NoneError) -> Self { - let err = HError::NoneError; - err - } -} +//impl From for HError { +//fn from(_error: std::option::NoneError) -> Self { +//let err = HError::NoneError; +//err +//} +//} impl From for HError { fn from(error: std::path::StripPrefixError) -> Self { - let err = HError::StripPrefixError{error: error }; + let err = HError::StripPrefixError { error: error }; err } } @@ -361,7 +373,6 @@ impl From for HError { } } - impl From for HError { fn from(error: std::num::ParseIntError) -> Self { let err = HError::ParseIntError(error); @@ -383,7 +394,6 @@ impl From for HError { } } - // MIME Errors #[derive(Fail, Debug, Clone)] @@ -392,7 +402,7 @@ pub enum MimeError { NoFileProvided, #[fail(display = "File access failed! Error: {}", _0)] AccessFailed(Box), - #[fail(display = "No MIME type found for this file",)] + #[fail(display = "No MIME type found for this file")] NoMimeFound, #[fail(display = "Paniced while trying to find MIME type for: {}!", _0)] Panic(String), @@ -404,7 +414,6 @@ impl From for HError { } } - impl From for HError { fn from(e: KeyBindError) -> Self { HError::KeyBind(e) @@ -417,7 +426,6 @@ impl From for HError { } } - #[derive(Fail, Debug, Clone)] pub enum KeyBindError { #[fail(display = "Movement has not been defined for this widget")] @@ -433,8 +441,7 @@ pub enum KeyBindError { #[fail(display = "Couldn't parse as either char or u8: {}", _0)] CharOrNumParseError(String), #[fail(display = "Wanted {}, but got {}!", _0, _1)] - CharOrNumWrongType(String, String) - + CharOrNumWrongType(String, String), } impl From for KeyBindError { diff --git a/src/file_browser.rs b/src/file_browser.rs index f3cec19..8d9134b 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -1,33 +1,33 @@ -use termion::event::Key; -use pathbuftools::PathBufTools; -use osstrtools::OsStrTools; use async_value::Stale; +use osstrtools::OsStrTools; +use pathbuftools::PathBufTools; +use termion::event::Key; -use std::io::Write; -use std::sync::{Arc, Mutex, RwLock}; -use std::path::PathBuf; +use std::collections::HashSet; use std::ffi::OsString; +use std::io::Write; use std::os::unix::ffi::OsStringExt; -use std::collections::HashSet; +use std::path::PathBuf; +use std::sync::{Arc, Mutex, RwLock}; +use crate::bookmarks::BMPopup; +use crate::coordinates::Coordinates; +use crate::dirty::Dirtyable; +use crate::fail::{ErrorLog, HError, HResult}; use crate::files::{File, Files}; +use crate::foldview::LogView; use crate::fscache::FsCache; -use crate::listview::{ListView, FileSource}; use crate::hbox::HBox; -use crate::widget::Widget; -use crate::tabview::{TabView, Tabbable}; -use crate::preview::{Previewer, AsyncWidget}; -use crate::textview::TextView; -use crate::fail::{HResult, HError, ErrorLog}; -use crate::widget::{Events, WidgetCore}; +use crate::listview::{FileSource, ListView}; +use crate::preview::{AsyncWidget, Previewer}; use crate::proclist::ProcView; -use crate::bookmarks::BMPopup; +use crate::stats::{FsExt, FsStat}; +use crate::tabview::{TabView, Tabbable}; use crate::term; use crate::term::ScreenExt; -use crate::foldview::LogView; -use crate::coordinates::Coordinates; -use crate::dirty::Dirtyable; -use crate::stats::{FsStat, FsExt}; +use crate::textview::TextView; +use crate::widget::Widget; +use crate::widget::{Events, WidgetCore}; #[derive(PartialEq)] pub enum FileBrowserWidgets { @@ -77,7 +77,7 @@ impl Widget for FileBrowserWidgets { match self { FileBrowserWidgets::FileList(widget) => widget.on_key(key), FileBrowserWidgets::Previewer(widget) => widget.on_key(key), - FileBrowserWidgets::Blank(widget) => widget.on_key(key) + FileBrowserWidgets::Blank(widget) => widget.on_key(key), } } } @@ -91,7 +91,7 @@ pub struct FileBrowser { bookmarks: Arc>, log_view: Arc>, fs_cache: FsCache, - fs_stat: Arc> + fs_stat: Arc>, } impl Tabbable for TabView { @@ -108,10 +108,10 @@ impl Tabbable for TabView { let proc_view = cur_tab.proc_view.clone(); let bookmarks = cur_tab.bookmarks.clone(); - let log_view = cur_tab.log_view.clone(); + let log_view = cur_tab.log_view.clone(); tab.proc_view = proc_view; tab.bookmarks = bookmarks; - tab.log_view = log_view; + tab.log_view = log_view; tab.fs_stat = cur_tab.fs_stat.clone(); self.push_widget(tab)?; @@ -139,15 +139,18 @@ impl Tabbable for TabView { } fn get_tab_names(&self) -> Vec> { - self.widgets.iter().map(|filebrowser| { - let path = filebrowser.cwd.path(); - let last_dir = path.components().last().unwrap(); - let dir_name = last_dir.as_os_str().to_string_lossy().to_string(); - Some(dir_name) - }).collect() + self.widgets + .iter() + .map(|filebrowser| { + let path = filebrowser.cwd.path(); + let last_dir = path.components().last().unwrap(); + let dir_name = last_dir.as_os_str().to_string_lossy().to_string(); + Some(dir_name) + }) + .collect() } - fn active_tab(& self) -> &Self::Tab { + fn active_tab(&self) -> &Self::Tab { self.active_tab_() } @@ -163,35 +166,38 @@ impl Tabbable for TabView { match self.active_tab_mut().on_key(key) { // returned by specific tab when called with ExecCmd action Err(HError::FileBrowserNeedTabFiles) => { - let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone()) + let tab_dirs = self + .widgets + .iter() + .map(|w| w.cwd.clone()) .collect::>(); let selected_files = self .widgets .iter() - .map(|w| { - w.selected_files().unwrap_or(vec![]) - }).collect(); + .map(|w| w.selected_files().unwrap_or(vec![])) + .collect(); self.widgets[self.active].exec_cmd(tab_dirs, selected_files) } - result @ _ => result + result @ _ => result, } } fn on_refresh(&mut self) -> HResult<()> { - let open_dirs = self.widgets - .iter() - .fold(HashSet::new(), |mut dirs, tab| { - tab.left_dir().map(|dir| dirs.insert(dir.clone())).ok(); - dirs.insert(tab.cwd.clone()); - tab.preview_widget() - .map(|preview| preview.get_file().map(|file| { + let open_dirs = self.widgets.iter().fold(HashSet::new(), |mut dirs, tab| { + tab.left_dir().map(|dir| dirs.insert(dir.clone())).ok(); + dirs.insert(tab.cwd.clone()); + tab.preview_widget() + .map(|preview| { + preview.get_file().map(|file| { if file.is_dir() { dirs.insert(file.clone()); } - })).ok(); - dirs - }); + }) + }) + .ok(); + dirs + }); self.active_tab_mut_().fs_cache.watch_only(open_dirs).log(); self.active_tab_mut_().fs_stat.write()?.refresh().log(); @@ -202,33 +208,45 @@ impl Tabbable for TabView { let show_hidden = self.core.config().show_hidden(); for tab in self.widgets.iter_mut() { - tab.left_async_widget_mut().map(|async_w| { - async_w.widget.on_ready(move |mut w, _| { - w.as_mut() - .map(|w| { - if w.content.show_hidden != show_hidden { - w.content.show_hidden = show_hidden; - w.content.recalculate_len(); - w.refresh().log(); - } - }).ok(); - Ok(()) - }).log(); - }).log(); - - tab.main_async_widget_mut().map(|async_w| { - async_w.widget.on_ready(move |mut w, _| { - w.as_mut() - .map(|w| { - if w.content.show_hidden != show_hidden { - w.content.show_hidden = show_hidden; - w.content.recalculate_len(); - w.refresh().log(); - } - }).ok(); - Ok(()) - }).log() - }).log(); + tab.left_async_widget_mut() + .map(|async_w| { + async_w + .widget + .on_ready(move |mut w, _| { + w.as_mut() + .map(|w| { + if w.content.show_hidden != show_hidden { + w.content.show_hidden = show_hidden; + w.content.recalculate_len(); + w.refresh().log(); + } + }) + .ok(); + Ok(()) + }) + .log(); + }) + .log(); + + tab.main_async_widget_mut() + .map(|async_w| { + async_w + .widget + .on_ready(move |mut w, _| { + w.as_mut() + .map(|w| { + if w.content.show_hidden != show_hidden { + w.content.show_hidden = show_hidden; + w.content.recalculate_len(); + w.refresh().log(); + } + }) + .ok(); + Ok(()) + }) + .log() + }) + .log(); tab.preview_widget_mut().map(|w| w.config_loaded()).ok(); tab.columns.set_ratios(self.core.config().ratios); @@ -237,13 +255,6 @@ impl Tabbable for TabView { } } - - - - - - - impl FileBrowser { pub fn new(core: &WidgetCore, cache: Option) -> HResult { let fs_cache = cache.unwrap_or_else(|| FsCache::new(core.get_sender())); @@ -261,11 +272,12 @@ impl FileBrowser { core_m.coordinates = list_coords[1].clone(); core_p.coordinates = list_coords[2].clone(); - let main_path = cwd.ancestors() - .take(1) - .map(|path| { - std::path::PathBuf::from(path) - }).last()?; + let main_path = cwd + .ancestors() + .take(1) + .map(|path| std::path::PathBuf::from(path)) + .last() + .ok_or_else(|| HError::NoneError)?; let left_path = main_path.parent().map(|p| p.to_path_buf()); let cache = fs_cache.clone(); @@ -297,11 +309,14 @@ impl FileBrowser { ListView::builder(core_l, source).build() }); - left_widget.widget.on_ready(move |_, stale| { - // To stop from drawing empty placeholder - stale.set_stale()?; - Ok(()) - }).log(); + left_widget + .widget + .on_ready(move |_, stale| { + // To stop from drawing empty placeholder + stale.set_stale()?; + Ok(()) + }) + .log(); let left_widget = FileBrowserWidgets::FileList(left_widget); columns.push_widget(left_widget); @@ -314,7 +329,6 @@ impl FileBrowser { columns.set_active(1).log(); columns.refresh().log(); - let cwd = File::new_from_path(&cwd).unwrap(); let proc_view = ProcView::new(&core); @@ -322,17 +336,17 @@ impl FileBrowser { let log_view = LogView::new(&core, vec![]); let fs_stat = FsStat::new().unwrap(); - - - Ok(FileBrowser { columns: columns, - cwd: cwd, - prev_cwd: None, - core: core.clone(), - proc_view: Arc::new(Mutex::new(proc_view)), - bookmarks: Arc::new(Mutex::new(bookmarks)), - log_view: Arc::new(Mutex::new(log_view)), - fs_cache: fs_cache, - fs_stat: Arc::new(RwLock::new(fs_stat)) }) + Ok(FileBrowser { + columns: columns, + cwd: cwd, + prev_cwd: None, + core: core.clone(), + proc_view: Arc::new(Mutex::new(proc_view)), + bookmarks: Arc::new(Mutex::new(bookmarks)), + log_view: Arc::new(Mutex::new(log_view)), + fs_cache: fs_cache, + fs_stat: Arc::new(RwLock::new(fs_stat)), + }) } pub fn enter_dir(&mut self) -> HResult<()> { @@ -341,15 +355,16 @@ impl FileBrowser { if file.is_dir() { let dir = file; match dir.is_readable() { - Ok(true) => {}, + Ok(true) => {} Ok(false) => { - let status = - format!("{}Stop right there, cowboy! Check your permisions!", - term::color_red()); + let status = format!( + "{}Stop right there, cowboy! Check your permisions!", + term::color_red() + ); self.core.show_status(&status).log(); return Ok(()); } - err @ Err(_) => err.log() + err @ Err(_) => err.log(), } self.preview_widget_mut()?.set_stale().log(); self.preview_widget_mut()?.cancel_animation().log(); @@ -360,36 +375,41 @@ impl FileBrowser { self.cwd = dir.clone(); let cache = self.fs_cache.clone(); - self.main_async_widget_mut()?.change_to(move |stale, core| { - let source = match previewer_files { - Some(files) => FileSource::Files(files), - None => FileSource::Path(dir) - }; - - ListView::builder(core, source) - .with_cache(cache) - .with_stale(stale.clone()) - .build() - }).log(); + self.main_async_widget_mut()? + .change_to(move |stale, core| { + let source = match previewer_files { + Some(files) => FileSource::Files(files), + None => FileSource::Path(dir), + }; + ListView::builder(core, source) + .with_cache(cache) + .with_stale(stale.clone()) + .build() + }) + .log(); let cache = self.fs_cache.clone(); let left_dir = self.cwd.parent_as_file()?; - self.left_async_widget_mut()?.change_to(move |stale, core| { - let source = match main_files { - Some(files) => FileSource::Files(files), - None => FileSource::Path(left_dir) - }; + self.left_async_widget_mut()? + .change_to(move |stale, core| { + let source = match main_files { + Some(files) => FileSource::Files(files), + None => FileSource::Path(left_dir), + }; - ListView::builder(core, source) - .with_cache(cache) - .with_stale(stale.clone()) - .build() - }).log(); + ListView::builder(core, source) + .with_cache(cache) + .with_stale(stale.clone()) + .build() + }) + .log(); } else { - self.preview_widget_mut().map(|preview| { - preview.cancel_animation().log(); - }).log(); + self.preview_widget_mut() + .map(|preview| { + preview.cancel_animation().log(); + }) + .log(); self.core.get_sender().send(Events::InputEnabled(false))?; self.core.screen.suspend().log(); @@ -403,12 +423,14 @@ impl FileBrowser { self.core.get_sender().send(Events::InputEnabled(true))?; match status { - Ok(status) => - self.core.show_status(&format!("\"{}\" exited with {}", - "xdg-open", status)).log(), - Err(err) => - self.core.show_status(&format!("Can't run this \"{}\": {}", - "xdg-open", err)).log() + Ok(status) => self + .core + .show_status(&format!("\"{}\" exited with {}", "xdg-open", status)) + .log(), + Err(err) => self + .core + .show_status(&format!("Can't run this \"{}\": {}", "xdg-open", err)) + .log(), } } @@ -418,13 +440,15 @@ impl FileBrowser { pub fn move_down_left_widget(&mut self) -> HResult<()> { let left_files_pos = self.left_widget()?.get_selection(); - let next_dir = self.get_left_files()? + let next_dir = self + .get_left_files()? .iter_files() .skip(left_files_pos + 1) .find(|&file| file.is_dir()) .cloned(); - self.main_widget_goto(&next_dir?).log(); + self.main_widget_goto(&next_dir.ok_or_else(|| HError::NoneError)?) + .log(); Ok(()) } @@ -432,7 +456,8 @@ impl FileBrowser { pub fn move_up_left_widget(&mut self) -> HResult<()> { let left_files_pos = self.left_widget()?.get_selection(); - let next_dir = self.get_left_files()? + let next_dir = self + .get_left_files()? .iter_files() .take(left_files_pos) .collect::>() @@ -441,7 +466,8 @@ impl FileBrowser { .find(|&file| file.is_dir()) .cloned(); - self.main_widget_goto(&next_dir?).log(); + self.main_widget_goto(&next_dir.ok_or_else(|| HError::NoneError)?) + .log(); Ok(()) } @@ -458,7 +484,7 @@ impl FileBrowser { cwd: cwd.clone(), cwd_files: None, tab_files: None, - tab_paths: None + tab_paths: None, }; self.proc_view.lock()?.run_proc_raw(cmd)?; @@ -466,7 +492,7 @@ impl FileBrowser { Ok(()) } - pub fn main_widget_goto_wait(&mut self, dir :&File) -> HResult<()> { + pub fn main_widget_goto_wait(&mut self, dir: &File) -> HResult<()> { self.main_widget_goto(&dir)?; // replace this with on_ready_mut() later @@ -480,9 +506,7 @@ impl FileBrowser { } pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> { - self.preview_widget_mut() - .map(|p| p.set_stale()) - .ok(); + self.preview_widget_mut().map(|p| p.set_stale()).ok(); let dir = dir.clone(); let cache = self.fs_cache.clone(); @@ -492,26 +516,25 @@ impl FileBrowser { let file_source = FileSource::Path(self.cwd.clone()); let main_async_widget = self.main_async_widget_mut()?; - main_async_widget.change_to(move |stale: &Stale, core| { - let view = ListView::builder(core, file_source) - .with_cache(cache) - .with_stale(stale.clone()) - .build()?; - - Ok(view) - }).log(); + main_async_widget + .change_to(move |stale: &Stale, core| { + let view = ListView::builder(core, file_source) + .with_cache(cache) + .with_stale(stale.clone()) + .build()?; + Ok(view) + }) + .log(); if let Ok(grand_parent) = self.cwd()?.parent_as_file() { self.left_widget_goto(&grand_parent).log(); } else { - self.left_async_widget_mut()?.change_to(move |_,_| { - HError::stale()? - }).log(); + self.left_async_widget_mut()? + .change_to(move |_, _| HError::stale()?) + .log(); } - - Ok(()) } @@ -526,14 +549,16 @@ impl FileBrowser { let cache = self.fs_cache.clone(); let file_source = FileSource::Path(dir.clone()); let left_async_widget = self.left_async_widget_mut()?; - left_async_widget.change_to(move |stale, core| { - let view = ListView::builder(core, file_source) - .with_cache(cache) - .with_stale(stale.clone()) - .build()?; + left_async_widget + .change_to(move |stale, core| { + let view = ListView::builder(core, file_source) + .with_cache(cache) + .with_stale(stale.clone()) + .build()?; - Ok(view) - }).log(); + Ok(view) + }) + .log(); Ok(()) } @@ -552,47 +577,54 @@ impl FileBrowser { let files = self.take_left_files(); let file_source = match files { Ok(files) => FileSource::Files(files), - Err(_) => FileSource::Path(new_cwd.clone()) + Err(_) => FileSource::Path(new_cwd.clone()), }; - self.main_async_widget_mut()?.change_to(move |stale, core| { - ListView::builder(core, file_source) - .select(main_selection) - .with_cache(cache) - .with_stale(stale.clone()) - .build() - }).log(); - - if let Ok(left_dir) = new_cwd.parent_as_file() { - let file_source = FileSource::Path(left_dir); - let cache = self.fs_cache.clone(); - self.left_async_widget_mut()?.change_to(move |stale, core| { + self.main_async_widget_mut()? + .change_to(move |stale, core| { ListView::builder(core, file_source) + .select(main_selection) .with_cache(cache) .with_stale(stale.clone()) .build() - }).log(); + }) + .log(); + + if let Ok(left_dir) = new_cwd.parent_as_file() { + let file_source = FileSource::Path(left_dir); + let cache = self.fs_cache.clone(); + self.left_async_widget_mut()? + .change_to(move |stale, core| { + ListView::builder(core, file_source) + .with_cache(cache) + .with_stale(stale.clone()) + .build() + }) + .log(); } else { // Just place a dummy in the left column - self.left_async_widget_mut()?.change_to(move |_, core| { - let files = Files::default(); - let source = FileSource::Files(files); - ListView::builder(core, source).build() - }).log(); - - self.left_async_widget_mut()?.widget.on_ready(move |_, stale| { - // To stop from drawing empty placeholder - stale.set_stale()?; - Ok(()) - }).log() + self.left_async_widget_mut()? + .change_to(move |_, core| { + let files = Files::default(); + let source = FileSource::Files(files); + ListView::builder(core, source).build() + }) + .log(); + + self.left_async_widget_mut()? + .widget + .on_ready(move |_, stale| { + // To stop from drawing empty placeholder + stale.set_stale()?; + Ok(()) + }) + .log() } - if let Ok(preview_files) = preview_files { - self.preview_widget_mut().map(|preview| { - preview.put_preview_files(preview_files, - previewer_selection) - }).ok(); + self.preview_widget_mut() + .map(|preview| preview.put_preview_files(preview_files, previewer_selection)) + .ok(); } } @@ -601,7 +633,7 @@ impl FileBrowser { } pub fn goto_prev_cwd(&mut self) -> HResult<()> { - let prev_cwd = self.prev_cwd.take()?; + let prev_cwd = self.prev_cwd.take().ok_or_else(|| HError::NoneError)?; self.main_widget_goto(&prev_cwd)?; Ok(()) } @@ -615,13 +647,19 @@ impl FileBrowser { fn get_boomark(&mut self) -> HResult { let cwd = &match self.prev_cwd.as_ref() { Some(cwd) => cwd, - None => &self.cwd - }.path.to_string_lossy().to_string(); + None => &self.cwd, + } + .path + .to_string_lossy() + .to_string(); - self.bookmarks.lock()?.set_coordinates(&self.core.coordinates).log(); + self.bookmarks + .lock()? + .set_coordinates(&self.core.coordinates) + .log(); loop { - let bookmark = self.bookmarks.lock()?.pick(cwd.to_string()); + let bookmark = self.bookmarks.lock()?.pick(cwd.to_string()); if let Err(HError::TerminalResizedError) = bookmark { self.core.screen.clear().log(); @@ -673,22 +711,24 @@ impl FileBrowser { } pub fn update_preview(&mut self) -> HResult<()> { - if !self.main_async_widget_mut()?.ready() { return Ok(()) } - if self.main_widget()? - .content - .len() == 0 { - self.preview_widget_mut()?.set_stale().log(); - return Ok(()); - } + if !self.main_async_widget_mut()?.ready() { + return Ok(()); + } + if self.main_widget()?.content.len() == 0 { + self.preview_widget_mut()?.set_stale().log(); + return Ok(()); + } let file = self.selected_file()?; // Don't even call previewer on empty files to save CPU cycles match (file.is_dir(), file.calculate_size()) { - (false, Ok((size, unit))) => if size == 0 && unit == "" { - self.preview_widget_mut()?.set_stale().log(); - return Ok(()); - }, + (false, Ok((size, unit))) => { + if size == 0 && unit == "" { + self.preview_widget_mut()?.set_stale().log(); + return Ok(()); + } + } _ => {} } @@ -698,13 +738,17 @@ impl FileBrowser { } pub fn set_left_selection(&mut self) -> HResult<()> { - if self.cwd.parent().is_none() { return Ok(()) } - if !self.left_async_widget_mut()?.ready() { return Ok(()) } + if self.cwd.parent().is_none() { + return Ok(()); + } + if !self.left_async_widget_mut()?.ready() { + return Ok(()); + } let selection = self.cwd()?.clone(); // Saves doing iteration to find file's position - if let Some(ref current_selection) = self.left_widget()?.current_item { + if let Some(ref current_selection) = self.left_widget()?.current_item { if current_selection.name == selection.name { return Ok(()); } @@ -713,12 +757,13 @@ impl FileBrowser { self.left_widget_mut()?.select_file(&selection); let selected_file = self.left_widget()?.selected_file(); - self.cwd.parent_as_file() - .map(|dir| { - self.fs_cache - .set_selection(dir.clone(), selected_file.clone()) - }).log(); - + self.cwd + .parent_as_file() + .map(|dir| { + self.fs_cache + .set_selection(dir.clone(), selected_file.clone()) + }) + .log(); Ok(()) } @@ -747,12 +792,13 @@ impl FileBrowser { pub fn save_selected_file(&self) -> HResult<()> { self.selected_file() - .map(|f| self.fs_cache.set_selection(self.cwd.clone(), - f))? + .map(|f| self.fs_cache.set_selection(self.cwd.clone(), f))? } pub fn save_tab_settings(&mut self) -> HResult<()> { - if !self.main_async_widget_mut()?.ready() { return Ok(()) } + if !self.main_async_widget_mut()?.ready() { + return Ok(()); + } if self.main_widget()?.content.len() > 0 { let files = self.get_files()?; @@ -763,7 +809,6 @@ impl FileBrowser { Ok(()) } - pub fn cwd(&self) -> HResult<&File> { Ok(&self.cwd) } @@ -788,77 +833,124 @@ impl FileBrowser { pub fn selected_files(&self) -> HResult> { let widget = self.main_widget()?; - let files = widget.content.get_selected().into_iter().map(|f| { - f.clone() - }).collect(); + let files = widget + .content + .get_selected() + .into_iter() + .map(|f| f.clone()) + .collect(); Ok(files) } pub fn main_async_widget_mut(&mut self) -> HResult<&mut AsyncWidget>> { - let widget = self.columns.active_widget_mut()?; + let widget = self + .columns + .active_widget_mut() + .ok_or_else(|| HError::NoneError)?; let widget = match widget { FileBrowserWidgets::FileList(filelist) => filelist, - _ => { HError::wrong_widget("previewer", "filelist")? } + _ => HError::wrong_widget("previewer", "filelist")?, }; Ok(widget) } pub fn main_widget(&self) -> HResult<&ListView> { - let widget = self.columns.active_widget()?; + let widget = self + .columns + .active_widget() + .ok_or_else(|| HError::NoneError)?; let widget = match widget { FileBrowserWidgets::FileList(filelist) => filelist.widget(), - _ => { HError::wrong_widget("previewer", "filelist")? } + _ => HError::wrong_widget("previewer", "filelist")?, }; widget } pub fn main_widget_mut(&mut self) -> HResult<&mut ListView> { - let widget = self.columns.active_widget_mut()?; + let widget = self + .columns + .active_widget_mut() + .ok_or_else(|| HError::NoneError)?; let widget = match widget { FileBrowserWidgets::FileList(filelist) => filelist.widget_mut(), - _ => { HError::wrong_widget("previewer", "filelist")? } + _ => HError::wrong_widget("previewer", "filelist")?, }; widget } pub fn left_async_widget_mut(&mut self) -> HResult<&mut AsyncWidget>> { - let widget = match self.columns.widgets.get_mut(0)? { + let widget = match self + .columns + .widgets + .get_mut(0) + .ok_or_else(|| HError::NoneError)? + { FileBrowserWidgets::FileList(filelist) => filelist, - _ => { return HError::wrong_widget("previewer", "filelist"); } + _ => { + return HError::wrong_widget("previewer", "filelist"); + } }; Ok(widget) } pub fn left_widget(&self) -> HResult<&ListView> { - let widget = match self.columns.widgets.get(0)? { + let widget = match self + .columns + .widgets + .get(0) + .ok_or_else(|| HError::NoneError)? + { FileBrowserWidgets::FileList(filelist) => filelist.widget(), - _ => { return HError::wrong_widget("previewer", "filelist"); } + _ => { + return HError::wrong_widget("previewer", "filelist"); + } }; widget } pub fn left_widget_mut(&mut self) -> HResult<&mut ListView> { - let widget = match self.columns.widgets.get_mut(0)? { + let widget = match self + .columns + .widgets + .get_mut(0) + .ok_or_else(|| HError::NoneError)? + { FileBrowserWidgets::FileList(filelist) => filelist.widget_mut(), - _ => { return HError::wrong_widget("previewer", "filelist"); } + _ => { + return HError::wrong_widget("previewer", "filelist"); + } }; widget } pub fn preview_widget(&self) -> HResult<&Previewer> { - match self.columns.widgets.get(2)? { + match self + .columns + .widgets + .get(2) + .ok_or_else(|| HError::NoneError)? + { FileBrowserWidgets::Previewer(previewer) => Ok(previewer), - _ => { return HError::wrong_widget("filelist", "previewer"); } + _ => { + return HError::wrong_widget("filelist", "previewer"); + } } } pub fn preview_widget_mut(&mut self) -> HResult<&mut Previewer> { - match self.columns.widgets.get_mut(2)? { + match self + .columns + .widgets + .get_mut(2) + .ok_or_else(|| HError::NoneError)? + { FileBrowserWidgets::Previewer(previewer) => Ok(previewer), - _ => { return HError::wrong_widget("filelist", "previewer"); } + _ => { + return HError::wrong_widget("filelist", "previewer"); + } } } @@ -870,24 +962,18 @@ impl FileBrowser { fn activate_main_widget(&mut self) { const MAIN_INDEX: usize = 1; - self.columns - .set_active(MAIN_INDEX) - .log(); + self.columns.set_active(MAIN_INDEX).log(); } fn activate_preview_widget(&mut self) { const PREVIEW_INDEX: usize = 2; - self.columns - .set_active(PREVIEW_INDEX) - .log(); + self.columns.set_active(PREVIEW_INDEX).log(); } pub fn toggle_colums(&mut self) { self.cancel_preview_animation(); self.activate_main_widget(); - self.columns - .toggle_zoom() - .log(); + self.columns.toggle_zoom().log(); } pub fn zoom_preview(&mut self) { @@ -896,11 +982,10 @@ impl FileBrowser { self.preview_widget_mut() .map(|preview| { preview.reload_text(); - }).log(); - - self.columns - .toggle_zoom() + }) .log(); + + self.columns.toggle_zoom().log(); } pub fn quit_with_dir(&self) -> HResult<()> { @@ -909,17 +994,20 @@ impl FileBrowser { let selected_file = selected_file.path.to_string_lossy(); let selected_files = self.selected_files()?; - let selected_files = selected_files.iter().map(|f| { - format!("\"{}\" ", &f.path.to_string_lossy()) - }).collect::(); + let selected_files = selected_files + .iter() + .map(|f| format!("\"{}\" ", &f.path.to_string_lossy())) + .collect::(); - let mut filepath = dirs_2::home_dir()?; + let mut filepath = dirs_2::home_dir().ok_or_else(|| HError::NoneError)?; filepath.push(".hunter_cwd"); - let output = format!("HUNTER_CWD=\"{}\"\nF=\"{}\"\nMF=({})\n", - cwd.to_str()?, - selected_file, - selected_files); + let output = format!( + "HUNTER_CWD=\"{}\"\nF=\"{}\"\nMF=({})\n", + cwd.to_str().ok_or_else(|| HError::NoneError)?, + selected_file, + selected_files + ); let mut file = std::fs::File::create(filepath)?; file.write(output.as_bytes())?; @@ -932,9 +1020,7 @@ impl FileBrowser { // Return and reset on cancel let orig_dir = self.cwd()?.clone(); let orig_dir_selected_file = self.selected_file()?; - let mut orig_dir_filter = self.main_widget()? - .content - .get_filter(); + let mut orig_dir_filter = self.main_widget()?.content.get_filter(); // For current dir let mut selected_file = Some(orig_dir_selected_file.clone()); @@ -944,10 +1030,11 @@ impl FileBrowser { let dir_restore = |s: &mut FileBrowser, filter: Option>, file: Option| { s.main_widget_mut() - .map(|mw| { - filter.map(|f| mw.set_filter(f)); - file.map(|f| mw.select_file(&f)); - }).log(); + .map(|mw| { + filter.map(|f| mw.set_filter(f)); + file.map(|f| mw.select_file(&f)); + }) + .log(); }; loop { @@ -980,9 +1067,7 @@ impl FileBrowser { if input.ends_with('/') { match input.as_str() { "../" => { - dir_restore(self, - filter.take(), - selected_file.take()); + dir_restore(self, filter.take(), selected_file.take()); self.go_back().log(); self.core.minibuffer_clear().log(); } @@ -990,9 +1075,7 @@ impl FileBrowser { let sel = self.selected_file()?; if sel.is_dir() { - dir_restore(self, - filter.take(), - selected_file.take()); + dir_restore(self, filter.take(), selected_file.take()); self.main_widget_goto(&sel)?; self.core.minibuffer_clear().log(); } @@ -1004,9 +1087,7 @@ impl FileBrowser { // Save current filter, if existing, before overwriting it // Type is Option>, because filter itself is Option<_> if filter.is_none() { - let dir_filter = self.main_widget()? - .content - .get_filter(); + let dir_filter = self.main_widget()?.content.get_filter(); filter = Some(dir_filter); } @@ -1015,8 +1096,7 @@ impl FileBrowser { selected_file = Some(self.selected_file()?); } - self.main_widget_mut()? - .set_filter(Some(input)); + self.main_widget_mut()?.set_filter(Some(input)); } // Restore original directory and filter/selection Cancelled => { @@ -1024,7 +1104,7 @@ impl FileBrowser { // Special case, because all others fail if directory isn't ready anyway self.main_async_widget_mut()? .widget - .on_ready(move |mw,_| { + .on_ready(move |mw, _| { let mw = mw?; mw.set_filter(orig_dir_filter.take()); mw.select_file(&orig_dir_selected_file); @@ -1063,8 +1143,8 @@ impl FileBrowser { selected_file = Some(self.selected_file()?); } } - }, - _ => { } + } + _ => {} } } @@ -1073,15 +1153,13 @@ impl FileBrowser { fn external_select(&mut self) -> HResult<()> { let shell = std::env::var("SHELL").unwrap_or("bash".into()); - let cmd = self.core - .config.read()? - .get()? - .select_cmd - .clone(); + let cmd = self.core.config.read()?.get()?.select_cmd.clone(); self.core.get_sender().send(Events::InputEnabled(false))?; self.core.screen.suspend().log(); - self.preview_widget().map(|preview| preview.cancel_animation()).log(); + self.preview_widget() + .map(|preview| preview.cancel_animation()) + .log(); let cmd_result = std::process::Command::new(shell) .arg("-c") @@ -1126,29 +1204,25 @@ impl FileBrowser { self.main_widget_goto(&dir).log(); - self.main_async_widget_mut()? - .widget - .on_ready(move |w, _| { - w?.select_file(&file); - Ok(()) - })?; + self.main_async_widget_mut()?.widget.on_ready(move |w, _| { + w?.select_file(&file); + Ok(()) + })?; } } else { - let msg = format!("Can't access path: {}!", - path.to_string_lossy()); + let msg = format!("Can't access path: {}!", path.to_string_lossy()); self.core.show_status(&msg).log(); } } else { let mut last_file = None; for file_path in paths { if !file_path.exists() { - let msg = format!("Can't find: {}", - file_path .to_string_lossy()); + let msg = format!("Can't find: {}", file_path.to_string_lossy()); self.core.show_status(&msg).log(); continue; } - let dir_path = file_path.parent()?; + let dir_path = file_path.parent().ok_or_else(|| HError::NoneError)?; if self.cwd.path != dir_path { let file_dir = File::new_from_path(&dir_path); @@ -1164,16 +1238,18 @@ impl FileBrowser { }); } - self.main_widget_mut().map(|w| { - last_file.map(|f| w.select_file(&f)); - w.content.set_dirty(); - }).log(); + self.main_widget_mut() + .map(|w| { + last_file.map(|f| w.select_file(&f)); + w.content.set_dirty(); + }) + .log(); } } else { self.core.show_status("External program failed!").log(); } } - Err(_) => self.core.show_status("Can't run external program!").log() + Err(_) => self.core.show_status("Can't run external program!").log(), } Ok(()) @@ -1181,15 +1257,13 @@ impl FileBrowser { fn external_cd(&mut self) -> HResult<()> { let shell = std::env::var("SHELL").unwrap_or("bash".into()); - let cmd = self.core - .config.read()? - .get()? - .cd_cmd - .clone(); + let cmd = self.core.config.read()?.get()?.cd_cmd.clone(); self.core.get_sender().send(Events::InputEnabled(false))?; self.core.screen.suspend().log(); - self.preview_widget().map(|preview| preview.cancel_animation()).log(); + self.preview_widget() + .map(|preview| preview.cancel_animation()) + .log(); let cmd_result = std::process::Command::new(shell) .arg("-c") @@ -1221,28 +1295,22 @@ impl FileBrowser { if path.is_dir() { let dir = File::new_from_path(&path)?; self.main_widget_goto(&dir).log(); - } - else { - let msg = format!("Can't access path: {}!", - path.to_string_lossy()); + } else { + let msg = format!("Can't access path: {}!", path.to_string_lossy()); self.core.show_status(&msg).log(); } - } else { self.core.show_status("External program failed!").log(); } } } - Err(_) => self.core.show_status("Can't run external program!").log() + Err(_) => self.core.show_status("Can't run external program!").log(), } Ok(()) } - - fn exec_cmd(&mut self, - tab_dirs: Vec, - tab_files: Vec>) -> HResult<()> { + fn exec_cmd(&mut self, tab_dirs: Vec, tab_files: Vec>) -> HResult<()> { let cwd = self.cwd()?.clone(); let selected_file = self.selected_file().ok(); let selected_files = self.selected_files().ok(); @@ -1269,7 +1337,7 @@ impl FileBrowser { cwd: cwd, cwd_files: cwd_files, tab_files: Some(tab_files), - tab_paths: Some(tab_dirs) + tab_paths: Some(tab_dirs), }; self.proc_view.lock()?.run_proc_subshell(cmd)?; @@ -1280,7 +1348,9 @@ impl FileBrowser { pub fn run_subshell(&mut self) -> HResult<()> { self.core.get_sender().send(Events::InputEnabled(false))?; - self.preview_widget().map(|preview| preview.cancel_animation()).log(); + self.preview_widget() + .map(|preview| preview.cancel_animation()) + .log(); self.core.screen.suspend().log(); let shell = std::env::var("SHELL").unwrap_or("bash".into()); @@ -1288,45 +1358,49 @@ impl FileBrowser { self.core.screen.activate().log(); - self.core.get_sender().send(Events::InputEnabled(true))?; match status { - Ok(status) => - self.core.show_status(&format!("\"{}\" exited with {}", - shell, status)).log(), - Err(err) => - self.core.show_status(&format!("Can't run this \"{}\": {}", - shell, err)).log() + Ok(status) => self + .core + .show_status(&format!("\"{}\" exited with {}", shell, status)) + .log(), + Err(err) => self + .core + .show_status(&format!("Can't run this \"{}\": {}", shell, err)) + .log(), } - - Ok(()) } pub fn show_procview(&mut self) -> HResult<()> { - self.preview_widget().map(|preview| preview.cancel_animation()).log(); + self.preview_widget() + .map(|preview| preview.cancel_animation()) + .log(); let procview = self.proc_view.clone(); loop { match procview.lock()?.popup() { // Ignore refresh Err(HError::RefreshParent) => continue, - Err(HError::TerminalResizedError) | - Err(HError::WidgetResizedError) => self.resize().log(), - _ => break + Err(HError::TerminalResizedError) | Err(HError::WidgetResizedError) => { + self.resize().log() + } + _ => break, } } Ok(()) } pub fn show_log(&mut self) -> HResult<()> { - self.preview_widget().map(|preview| preview.cancel_animation()).log(); + self.preview_widget() + .map(|preview| preview.cancel_animation()) + .log(); loop { let res = self.log_view.lock()?.popup(); if let Err(HError::RefreshParent) = res { - continue + continue; } if let Err(HError::TerminalResizedError) = res { @@ -1334,7 +1408,7 @@ impl FileBrowser { continue; } - break + break; } Ok(()) @@ -1342,17 +1416,17 @@ impl FileBrowser { pub fn quick_action(&self) -> HResult<()> { let files = self.selected_files()?; - let files = if files.len() > 0 { files } - else { vec![self.selected_file()?.clone()] }; + let files = if files.len() > 0 { + files + } else { + vec![self.selected_file()?.clone()] + }; let sender = self.core.get_sender(); let core = self.preview_widget()?.get_core()?.clone(); let proc_view = self.proc_view.clone(); - crate::quick_actions::open(files, - sender, - core, - proc_view)?; + crate::quick_actions::open(files, sender, core, proc_view)?; Ok(()) } @@ -1361,24 +1435,27 @@ impl FileBrowser { let ypos = self.get_coordinates()?.position().y(); let file = self.selected_file()?; - let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into()); let user = file.pretty_user().unwrap_or("NOUSER".into()); let group = file.pretty_group().unwrap_or("NOGROUP".into()); let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into()); let target = if let Some(target) = &file.target { "--> ".to_string() + &target.short_string() - } else { "".to_string() }; + } else { + "".to_string() + }; let main_widget = self.main_widget()?; let selection = main_widget.get_selection() + 1; let file_count = main_widget.content.len(); let file_count = format!("{}", file_count); let digits = file_count.len(); - let file_count = format!("{:digits$}/{:digits$}", - selection, - file_count, - digits = digits); + let file_count = format!( + "{:digits$}/{:digits$}", + selection, + file_count, + digits = digits + ); let count_xpos = xsize - file_count.len() as u16; let count_ypos = ypos + self.get_coordinates()?.ysize(); @@ -1387,32 +1464,32 @@ impl FileBrowser { let dev = fs.get_dev().unwrap_or(String::from("")); let free_space = fs.get_free(); let total_space = fs.get_total(); - let space = format!("{}{} / {}", - dev, - free_space, - total_space); + let space = format!("{}{} / {}", dev, free_space, total_space); let space_xpos = count_xpos - space.len() as u16 - 5; // - 3; - let status = format!("{} {}:{} {}{} {}{}", - permissions, - user, - group, - crate::term::header_color(), - mtime, - crate::term::color_yellow(), - target + let status = format!( + "{} {}:{} {}{} {}{}", + permissions, + user, + group, + crate::term::header_color(), + mtime, + crate::term::color_yellow(), + target + ); + let status = crate::term::sized_string_u(&status, (xsize - 1) as usize); + + let status = format!( + "{}{}{}{}{}{} | {}", + status, + crate::term::header_color(), + crate::term::goto_xy(space_xpos, count_ypos), + crate::term::color_orange(), + space, + crate::term::header_color(), + file_count ); - let status = crate::term::sized_string_u(&status, (xsize-1) as usize); - - let status = format!("{}{}{}{}{}{} | {}", - status, - crate::term::header_color(), - crate::term::goto_xy(space_xpos, count_ypos), - crate::term::color_orange(), - space, - crate::term::header_color(), - file_count); Ok(status) } @@ -1443,42 +1520,42 @@ impl Widget for FileBrowser { let fcolor = file.get_color(); let color = if file.is_dir() { - crate::term::highlight_color() } - else { + crate::term::highlight_color() + } else { match fcolor { Some(color) => color, - None => crate::term::normal_color() + None => crate::term::normal_color(), } }; let path = self.cwd.short_string(); let mut path = path; - if &path == "" { path.clear(); } - if &path == "~/" { path.pop(); } - if &path == "/" { path.pop(); } - + if &path == "" { + path.clear(); + } + if &path == "~/" { + path.pop(); + } + if &path == "/" { + path.pop(); + } - let pretty_path = format!("{}/{}{}", path, &color, name ); + let pretty_path = format!("{}/{}{}", path, &color, name); let sized_path = crate::term::sized_string(&pretty_path, xsize); Ok(sized_path.to_string()) } fn render_footer(&self) -> HResult { let xsize = term::xsize_u(); - let mut status = self.get_core()? - .status_bar_content - .lock()?; - let status = status.as_mut() - .take(); - let active = self.columns - .active - .unwrap_or(1); + let mut status = self.get_core()?.status_bar_content.lock()?; + let status = status.as_mut().take(); + let active = self.columns.active.unwrap_or(1); match (status, active) { (Some(status), _) => Ok(term::sized_string_u(&status, xsize)), - (_, 2) => self.preview_widget()?.render_footer(), - _ => self.get_footer(), + (_, 2) => self.preview_widget()?.render_footer(), + _ => self.get_footer(), } } @@ -1487,7 +1564,9 @@ impl Widget for FileBrowser { self.columns.refresh().log(); self.set_left_selection().log(); self.set_cwd().log(); - if !self.columns.zoom_active { self.update_preview().log(); } + if !self.columns.zoom_active { + self.update_preview().log(); + } self.columns.refresh().log(); Ok(()) } @@ -1511,28 +1590,33 @@ impl Widget for FileBrowser { return Ok(()); } (_, Some(2)) => { - self.columns.active_widget_mut()?.on_key(key)?; + self.columns + .active_widget_mut() + .ok_or_else(|| HError::NoneError)? + .on_key(key)?; return Ok(()); } _ => {} } match self.do_key(key) { - Err(HError::WidgetUndefinedKeyError{..}) => { + Err(HError::WidgetUndefinedKeyError { .. }) => { match self.main_widget_mut()?.on_key(key) { Ok(_) => { self.save_tab_settings()?; } - Err(HError::WidgetUndefinedKeyError{..}) => { + Err(HError::WidgetUndefinedKeyError { .. }) => { self.preview_widget_mut()?.on_key(key)? } - e @ _ => e? + e @ _ => e?, } } - e @ _ => e? + e @ _ => e?, }; - if !self.columns.zoom_active { self.update_preview().log(); } + if !self.columns.zoom_active { + self.update_preview().log(); + } Ok(()) } } @@ -1540,7 +1624,7 @@ impl Widget for FileBrowser { use crate::keybind::{Acting, Bindings, FileBrowserAction, Movement}; impl Acting for FileBrowser { - type Action=FileBrowserAction; + type Action = FileBrowserAction; fn search_in(&self) -> Bindings { self.core.config().keybinds.filebrowser @@ -1587,7 +1671,7 @@ impl Acting for FileBrowser { ToggleColumns => self.toggle_colums(), ZoomPreview => self.zoom_preview(), // Tab implementation needs to call exec_cmd because ALL files are needed - ExecCmd => Err(HError::FileBrowserNeedTabFiles)? + ExecCmd => Err(HError::FileBrowserNeedTabFiles)?, } Ok(()) } diff --git a/src/files.rs b/src/files.rs index f78f067..88394ce 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,42 +1,37 @@ use std::cmp::Ord; use std::collections::{HashMap, HashSet}; -use std::ops::Index; +use std::ffi::OsStr; use std::fs::Metadata; +use std::hash::{Hash, Hasher}; +use std::ops::Index; use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; -use std::sync::{Arc, RwLock}; -use std::sync::mpsc::Sender; -use std::hash::{Hash, Hasher}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::ffi::OsStr; +use std::sync::mpsc::Sender; +use std::sync::{Arc, RwLock}; +use chrono::TimeZone; use failure; +use failure::Error; use failure::Fail; use lscolors::LsColors; -use tree_magic_fork; -use users::{get_current_username, - get_current_groupname, - get_user_by_uid, - get_group_by_gid}; -use chrono::TimeZone; -use failure::Error; -use rayon::{ThreadPool, ThreadPoolBuilder}; -use natord::compare; use mime_guess; +use natord::compare; +use nix::{dir::*, fcntl::OFlag, sys::stat::Mode}; use rayon::prelude::*; -use nix::{dir::*, - fcntl::OFlag, - sys::stat::Mode}; +use rayon::{ThreadPool, ThreadPoolBuilder}; +use tree_magic_fork; +use users::{get_current_groupname, get_current_username, get_group_by_gid, get_user_by_uid}; -use pathbuftools::PathBufTools; use async_value::{Async, Stale, StopIter}; +use pathbuftools::PathBufTools; -use crate::fail::{HResult, HError, ErrorLog}; use crate::dirty::{DirtyBit, Dirtyable}; -use crate::widget::Events; -use crate::icon::Icons; +use crate::fail::{ErrorLog, HError, HResult}; use crate::fscache::{FsCache, FsEvent}; +use crate::icon::Icons; +use crate::widget::Events; lazy_static! { static ref COLORS: LsColors = LsColors::from_env().unwrap_or_default(); @@ -52,7 +47,7 @@ pub fn tick_str() -> &'static str { 0 => " ", 1 => ". ", 2 => ".. ", - _ => "..." + _ => "...", } } @@ -60,7 +55,7 @@ pub fn start_ticking(sender: Sender) { use std::time::Duration; IOTICK_CLIENTS.fetch_add(1, Ordering::Relaxed); - if IOTICK_CLIENTS.load(Ordering::Relaxed) == 1 { + if IOTICK_CLIENTS.load(Ordering::Acquire) == 1 { std::thread::spawn(move || { IOTICK.store(0, Ordering::Relaxed); @@ -72,11 +67,10 @@ pub fn start_ticking(sender: Sender) { IOTICK.fetch_add(1, Ordering::Relaxed); // Send refresh event before sleeping - sender.send(crate::widget::Events::WidgetReady) - .unwrap(); + sender.send(crate::widget::Events::WidgetReady).unwrap(); // All jobs done? - if IOTICK_CLIENTS.load(Ordering::Relaxed) == 0 { + if IOTICK_CLIENTS.load(Ordering::Acquire) == 0 { IOTICK.store(0, Ordering::Relaxed); return; } @@ -128,9 +122,7 @@ pub fn load_tags() -> HResult<()> { } let tags = std::fs::read_to_string(tag_path)?; - let mut tags = tags.lines() - .map(PathBuf::from) - .collect::>(); + let mut tags = tags.lines().map(PathBuf::from).collect::>(); tags.sort(); let mut tag_lock = TAGS.write()?; tag_lock.0 = true; @@ -153,16 +145,21 @@ pub fn import_tags() -> HResult<()> { pub fn check_tag(path: &PathBuf) -> HResult { tags_loaded()?; - let tagged = TAGS.read()?.1.binary_search(path) - .map_or_else(|_| false, - |_| true); + let tagged = TAGS + .read()? + .1 + .binary_search(path) + .map_or_else(|_| false, |_| true); Ok(tagged) } pub fn tags_loaded() -> HResult<()> { let loaded = TAGS.read()?.0; - if loaded { Ok(()) } - else { HError::tags_not_loaded() } + if loaded { + Ok(()) + } else { + HError::tags_not_loaded() + } } #[derive(Derivative)] @@ -170,15 +167,12 @@ pub fn tags_loaded() -> HResult<()> { pub struct RefreshPackage { pub new_files: Option>, pub new_len: usize, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] - pub jobs: Vec + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] + pub jobs: Vec, } - - - impl RefreshPackage { fn new(mut files: Files, events: Vec) -> RefreshPackage { use FsEvent::*; @@ -219,7 +213,6 @@ impl RefreshPackage { // Drop would set this stale after the function returns let stale = files.stale.take().unwrap(); - for event in events.into_iter().stop_stale(stale.clone()) { match event { Create(mut file) => { @@ -239,7 +232,7 @@ impl RefreshPackage { files.files[fpos].rename(&new.path).log(); let job = files.files[fpos].refresh_meta_job(); jobs.push(job); - } + } } Remove(file) => { if let Some(_) = file_pos_map.get(&file) { @@ -254,8 +247,8 @@ impl RefreshPackage { return RefreshPackage { new_files: None, new_len: 0, - jobs: jobs - } + jobs: jobs, + }; } if deleted_files.len() > 0 { @@ -270,9 +263,9 @@ impl RefreshPackage { files.sort(); // Need to unpack this to prevent issue with recursive Files type - // Also, if no files remain add placeholder and set len + // Also, if no files remain add placeholder and set len let (files, new_len) = if files.len() > 0 { - (std::mem::take(&mut files.files), files.len) + (std::mem::take(&mut files.files), files.len) } else { let placeholder = File::new_placeholder(&files.directory.path).unwrap(); files.files.push(placeholder); @@ -282,15 +275,17 @@ impl RefreshPackage { RefreshPackage { new_files: Some(files), new_len: new_len, - jobs: jobs + jobs: jobs, } } } // Tuple that stores path and "slots" to store metaadata in -pub type Job = (PathBuf, - Option>>>, - Option>); +pub type Job = ( + PathBuf, + Option>>>, + Option>, +); #[derive(Derivative)] #[derivative(PartialEq, Eq, Hash, Clone, Debug)] @@ -298,13 +293,13 @@ pub struct Files { pub directory: File, pub files: Vec, pub len: usize, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] pub pending_events: Arc>>, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] pub refresh: Option>, pub meta_upto: Option, pub sort: SortBy, @@ -314,18 +309,18 @@ pub struct Files { pub filter: Option, pub filter_selected: bool, pub dirty: DirtyBit, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] pub jobs: Vec, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] pub cache: Option, - #[derivative(Debug="ignore")] - #[derivative(PartialEq="ignore")] - #[derivative(Hash="ignore")] - pub stale: Option + #[derivative(Debug = "ignore")] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] + pub stale: Option, } impl Index for Files { @@ -335,7 +330,6 @@ impl Index for Files { } } - impl Dirtyable for Files { fn is_dirty(&self) -> bool { self.dirty.is_dirty() @@ -370,7 +364,7 @@ impl Default for Files { dirty: DirtyBit::new(), jobs: vec![], cache: None, - stale: None + stale: None, } } } @@ -378,13 +372,10 @@ impl Default for Files { // Stop processing stuff when Files is dropped impl Drop for Files { fn drop(&mut self) { - self.stale - .as_ref() - .map(|s| s.set_stale()); + self.stale.as_ref().map(|s| s.set_stale()); } } - #[cfg(target_os = "linux")] #[repr(C)] #[derive(Clone, Debug)] @@ -396,7 +387,6 @@ pub struct linux_dirent { pub d_name: [u8; 0], } - // This arcane spell hastens the target by around 30%. // It uses quite a bit of usafe code, mostly to call into libc and @@ -415,8 +405,11 @@ pub struct linux_dirent { // report the kind of file in d_type. Currently that means calling // stat on ALL files and ithrowing away the result. This is wasteful. #[cfg(target_os = "linux")] -pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result, FileError> -{ +pub fn from_getdents( + fd: i32, + path: &Path, + nothidden: &AtomicUsize, +) -> Result, FileError> { use libc::SYS_getdents64; // Buffer size was chosen after measuring different sizes and 4k seemed best @@ -435,10 +428,9 @@ pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result), Skip, Done, - Err(FileError) + Err(FileError), } - let result = crossbeam::scope(|s| { loop { // Returns number of bytes written to buffer @@ -449,8 +441,7 @@ pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result(&format!("Couldn't read dents from: {}", - &pathstr)).ok(); + HError::log::<()>(&format!("Couldn't read dents from: {}", &pathstr)).ok(); break; } @@ -475,22 +466,26 @@ pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result buffer[n + len(buffer[n]) - let d: &linux_dirent = unsafe { - std::mem::transmute::(bufptr + bpos ) - }; + let d: &linux_dirent = + unsafe { std::mem::transmute::(bufptr + bpos) }; // Name lenegth is overallocated, true length can be found by checking with strlen - let name_len = d.d_reclen as usize - - std::mem::size_of::() - - std::mem::size_of::() - - std::mem::size_of::() - - std::mem::size_of::(); + let name_len = d.d_reclen as usize + - std::mem::size_of::() + - std::mem::size_of::() + - std::mem::size_of::() + - std::mem::size_of::(); // OOB!!! if bpos + name_len > BUFFER_SIZE { - HError::log::<()>(&format!("WARNING: Name for file was out of bounds in: {}", - path.to_string_lossy())).ok(); - return DentStatus::Err(FileError::GetDents(path.to_string_lossy().to_string())); + HError::log::<()>(&format!( + "WARNING: Name for file was out of bounds in: {}", + path.to_string_lossy() + )) + .ok(); + return DentStatus::Err(FileError::GetDents( + path.to_string_lossy().to_string(), + )); } // Add length of current dirent to the current offset @@ -501,11 +496,12 @@ pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result Result Result stat, - Err(_) => return DentStatus::Err(FileError::GetDents(path.to_string_lossy() - .to_string())) - }; + let stat = match fstatat(fd, &path, flags) { + Ok(stat) => stat, + Err(_) => { + return DentStatus::Err(FileError::GetDents( + path.to_string_lossy().to_string(), + )) + } + }; let mode = SFlag::from_bits_truncate(stat.st_mode); match mode & SFlag::S_IFMT { SFlag::S_IFDIR => (Kind::Directory, None), - _ => (Kind::File, None) + _ => (Kind::File, None), } } 10 => { // This is a link - let target = nix::fcntl::readlinkat(fd, &path) - .map(PathBuf::from).ok(); - let target_kind = - match path.is_dir() { - true => Kind::Directory, - false => Kind::File - }; + let target = nix::fcntl::readlinkat(fd, &path).map(PathBuf::from).ok(); + let target_kind = match path.is_dir() { + true => Kind::Directory, + false => Kind::File, + }; (target_kind, target) } - _ => (Kind::File, None) + _ => (Kind::File, None), }; - let name = name.to_str() - .map(|n| String::from(n)) - .unwrap_or_else(|| name.to_string_lossy().to_string()); + let name = name + .to_str() + .map(|n| String::from(n)) + .unwrap_or_else(|| name.to_string_lossy().to_string()); let hidden = name.as_bytes()[0] == b'.'; @@ -598,12 +594,10 @@ pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Result Ok(std::mem::take(&mut *files.lock().unwrap())), - Err(_) => Err(FileError::GetDents(path.to_string_lossy().to_string())) + Err(_) => Err(FileError::GetDents(path.to_string_lossy().to_string())), } } - - impl Files { // Use getdents64 on Linux #[cfg(target_os = "linux")] @@ -612,9 +606,7 @@ impl Files { let nonhidden = AtomicUsize::default(); - let dir = Dir::open(path.clone(), - OFlag::O_DIRECTORY, - Mode::empty()) + let dir = Dir::open(path.clone(), OFlag::O_DIRECTORY, Mode::empty()) .map_err(|e| FileError::OpenDir(e))?; let direntries = from_getdents(dir.as_raw_fd(), path, &nonhidden)?; @@ -626,7 +618,6 @@ impl Files { let mut files = Files::default(); files.directory = File::new_from_path(&path)?; - files.files = direntries; files.len = nonhidden.load(Ordering::Relaxed); files.stale = Some(stale); @@ -634,21 +625,18 @@ impl Files { Ok(files) } - #[cfg(not(target_os = "linux"))] pub fn new_from_path_cancellable(path: &Path, stale: Stale) -> HResult { use std::os::unix::io::AsRawFd; let nonhidden = AtomicUsize::default(); - let mut dir = Dir::open(path.clone(), - OFlag::O_DIRECTORY, - Mode::empty()) + let mut dir = Dir::open(path.clone(), OFlag::O_DIRECTORY, Mode::empty()) .map_err(|e| FileError::OpenDir(e))?; let dirfd = dir.as_raw_fd(); - let direntries = dir + let direntries = dir .iter() .stop_stale(stale.clone()) .map(|f| { @@ -659,7 +647,7 @@ impl Files { } Ok(f) }) - .collect::>() + .collect::>() .map_err(|e| FileError::ReadFiles(e))?; if stale.is_stale()? { @@ -681,27 +669,28 @@ impl Files { let cache = match self.cache.clone() { Some(cache) => cache, - None => return + None => return, }; - let mut jobs = self.iter_files_mut() - .collect::>() - .into_par_iter() - .skip(from) - .take(n) - .filter_map(|f| f.prepare_meta_job(&cache)) - .collect::>(); + let mut jobs = self + .iter_files_mut() + .collect::>() + .into_par_iter() + .skip(from) + .take(n) + .filter_map(|f| f.prepare_meta_job(&cache)) + .collect::>(); self.jobs.append(&mut jobs); } pub fn run_jobs(&mut self, sender: Sender) { let jobs = std::mem::take(&mut self.jobs); - let stale = self.stale - .clone() - .unwrap_or_else(Stale::new); + let stale = self.stale.clone().unwrap_or_else(Stale::new); - if jobs.len() == 0 { return; } + if jobs.len() == 0 { + return; + } std::thread::spawn(move || { let pool = get_pool(); @@ -710,9 +699,7 @@ impl Files { start_ticking(sender); pool.scope_fifo(move |s| { - for (path, mslot, dirsize) in jobs.into_iter() - .stop_stale(stale.clone()) - { + for (path, mslot, dirsize) in jobs.into_iter().stop_stale(stale.clone()) { s.spawn_fifo(move |_| { if let Some(mslot) = mslot { if let Ok(meta) = std::fs::symlink_metadata(&path) { @@ -721,9 +708,7 @@ impl Files { } if let Some(dirsize) = dirsize { - let size = Dir::open(&path, - OFlag::O_DIRECTORY, - Mode::empty()) + let size = Dir::open(&path, OFlag::O_DIRECTORY, Mode::empty()) .map(|mut d| d.iter().count()) .map_err(|e| FileError::OpenDir(e)) .log_and() @@ -751,36 +736,33 @@ impl Files { self.files.get_mut(index + hidden_in_between) } - pub fn par_iter_files(&self) -> impl ParallelIterator { + pub fn par_iter_files(&self) -> impl ParallelIterator { let filter_fn = self.filter_fn(); - self.files - .par_iter() - .filter(move |f| filter_fn(f)) + self.files.par_iter().filter(move |f| filter_fn(f)) } - pub fn iter_files(&self) -> impl Iterator { + pub fn iter_files(&self) -> impl Iterator { let filter_fn = self.filter_fn(); - self.files - .iter() - .filter(move |&f| filter_fn(f)) + self.files.iter().filter(move |&f| filter_fn(f)) } pub fn files_in_between(&self, pos: usize, n_before: usize) -> usize { let filter_fn = self.filter_fn(); - self.files[..pos].iter() - .rev() - .enumerate() - .filter(|(_, f)| filter_fn(f)) - .take(n_before) - .map(|(i, _)| i + 1) - .last() - .unwrap_or(0) + self.files[..pos] + .iter() + .rev() + .enumerate() + .filter(|(_, f)| filter_fn(f)) + .take(n_before) + .map(|(i, _)| i + 1) + .last() + .unwrap_or(0) } - pub fn iter_files_from(&self, from: &File, n_before: usize) -> impl Iterator { + pub fn iter_files_from(&self, from: &File, n_before: usize) -> impl Iterator { let fpos = self.find_file(from).unwrap_or(0); let files_in_between = self.files_in_between(fpos, n_before); @@ -792,7 +774,11 @@ impl Files { .filter(move |f| filter_fn(f)) } - pub fn iter_files_mut_from(&mut self, from: &File, n_before: usize) -> impl Iterator { + pub fn iter_files_mut_from( + &mut self, + from: &File, + n_before: usize, + ) -> impl Iterator { let fpos = self.find_file(from).unwrap_or(0); let files_in_between = self.files_in_between(fpos, n_before); @@ -803,12 +789,10 @@ impl Files { .filter(move |f| filter_fn(f)) } - pub fn iter_files_mut(&mut self) -> impl Iterator { + pub fn iter_files_mut(&mut self) -> impl Iterator { let filter_fn = self.filter_fn(); - self.files - .iter_mut() - .filter(move |f| filter_fn(f)) + self.files.iter_mut().filter(move |f| filter_fn(f)) } #[allow(trivial_bounds)] @@ -818,11 +802,10 @@ impl Files { let show_hidden = self.show_hidden; move |f| { - f.kind == Kind::Placeholder || - !(filter.is_some() && - !f.name.contains(filter.as_ref().unwrap())) && - (!filter_selected || f.selected) && - !(!show_hidden && f.name.starts_with(".")) + f.kind == Kind::Placeholder + || !(filter.is_some() && !f.name.contains(filter.as_ref().unwrap())) + && (!filter_selected || f.selected) + && !(!show_hidden && f.name.starts_with(".")) } } @@ -833,15 +816,12 @@ impl Files { let dirs_first = self.dirs_first.clone(); let sort = self.sort.clone(); - let dircmp = move |a: &File, b: &File| { - match (a.is_dir(), b.is_dir()) { - (true, false) if dirs_first => Less, - (false, true) if dirs_first => Greater, - _ => Equal - } + let dircmp = move |a: &File, b: &File| match (a.is_dir(), b.is_dir()) { + (true, false) if dirs_first => Less, + (false, true) if dirs_first => Greater, + _ => Equal, }; - let reverse = self.reverse; let namecmp = move |a: &File, b: &File| { let (a, b) = match reverse { @@ -865,10 +845,10 @@ impl Files { let b_meta = b_meta.as_ref().unwrap(); match a_meta.size() == b_meta.size() { true => compare(&b.name, &a.name), - false => b_meta.size().cmp(&a_meta.size()) + false => b_meta.size().cmp(&a_meta.size()), } } - _ => Equal + _ => Equal, } }; @@ -885,41 +865,33 @@ impl Files { let b_meta = b_meta.as_ref().unwrap(); match a_meta.mtime() == b_meta.mtime() { true => compare(&b.name, &a.name), - false => b_meta.mtime().cmp(&a_meta.mtime()) + false => b_meta.mtime().cmp(&a_meta.mtime()), } } - _ => Equal + _ => Equal, } }; - move |a, b| match sort { - SortBy::Name => { - match dircmp(a, b) { - Equal => namecmp(a, b), - ord @ _ => ord - } + SortBy::Name => match dircmp(a, b) { + Equal => namecmp(a, b), + ord @ _ => ord, + }, + SortBy::Size => match dircmp(a, b) { + Equal => sizecmp(a, b), + ord @ _ => ord, + }, + SortBy::MTime => match dircmp(a, b) { + Equal => timecmp(a, b), + ord @ _ => ord, }, - SortBy::Size => { - match dircmp(a, b) { - Equal => sizecmp(a, b), - ord @ _ => ord - } - } - SortBy::MTime => { - match dircmp(a, b) { - Equal => timecmp(a, b), - ord @ _ => ord - } - } } } pub fn sort(&mut self) { let sort = self.sorter(); - self.files - .par_sort_unstable_by(sort); + self.files.par_sort_unstable_by(sort); } pub fn cycle_sort(&mut self) { @@ -948,11 +920,20 @@ impl Files { self.recalculate_len(); } + fn remove_placeholder_and_update_files(&mut self, placeholder: &File) -> Option { + let position = self + .files + .iter() + .position(|element| *element == *placeholder)?; + Some(self.files.remove(position)) + } + fn remove_placeholder(&mut self) { let dirpath = self.directory.path.clone(); - self.find_file_with_path(&dirpath).cloned() + self.find_file_with_path(&dirpath) + .cloned() .map(|placeholder| { - self.files.remove_item(&placeholder); + self.remove_placeholder_and_update_files(&placeholder); if self.len > 0 { self.len -= 1; } @@ -971,7 +952,7 @@ impl Files { self.stale.as_ref().map(|s| s.set_fresh()); refresh.pull_async()?; let mut refresh = refresh.value?; - self.files = refresh.new_files.take()?; + self.files = refresh.new_files.take().ok_or_else(|| HError::NoneError)?; self.jobs.append(&mut refresh.jobs); if refresh.new_len != self.len() { self.len = refresh.new_len; @@ -982,7 +963,7 @@ impl Files { } } - return Ok(None) + return Ok(None); } pub fn process_fs_events(&mut self, sender: Sender) -> HResult<()> { @@ -998,9 +979,7 @@ impl Files { Ok(refresh) }); - refresh.on_ready(move |_,_| { - Ok(sender.send(Events::WidgetReady)?) - })?; + refresh.on_ready(move |_, _| Ok(sender.send(Events::WidgetReady)?))?; refresh.run()?; @@ -1012,7 +991,11 @@ impl Files { pub fn path_in_here(&self, path: &Path) -> HResult { let dir = &self.directory.path; - let path = if path.is_dir() { path } else { path.parent().unwrap() }; + let path = if path.is_dir() { + path + } else { + path.parent().unwrap() + }; if dir == path { Ok(true) } else { @@ -1022,7 +1005,8 @@ impl Files { pub fn find_file(&self, file: &File) -> Option { let comp = self.sorter(); - let pos = self.files + let pos = self + .files .binary_search_by(|probe| comp(probe, file)) .ok()?; @@ -1069,9 +1053,8 @@ impl Files { self.len } - pub fn get_selected(&self) -> impl Iterator { - self.iter_files() - .filter(|f| f.is_selected()) + pub fn get_selected(&self) -> impl Iterator { + self.iter_files().filter(|f| f.is_selected()) } } @@ -1079,7 +1062,7 @@ impl Files { pub enum Kind { Directory, File, - Placeholder + Placeholder, } impl std::fmt::Display for SortBy { @@ -1100,7 +1083,6 @@ pub enum SortBy { MTime, } - impl PartialEq for File { fn eq(&self, other: &File) -> bool { if self.path == other.path { @@ -1132,7 +1114,6 @@ impl std::default::Default for File { } } - #[derive(Clone)] pub struct File { pub name: String, @@ -1147,15 +1128,17 @@ pub struct File { } impl File { - pub fn new( - name: &str, - path: PathBuf) -> File { + pub fn new(name: &str, path: PathBuf) -> File { let hidden = name.starts_with("."); File { name: name.to_string(), hidden: hidden, - kind: if path.is_dir() { Kind::Directory } else { Kind::File }, + kind: if path.is_dir() { + Kind::Directory + } else { + Kind::File + }, path: path, dirsize: None, target: None, @@ -1165,9 +1148,7 @@ impl File { } } - pub fn new_from_nixentry(direntry: Entry, - path: &Path, - dirfd: i32) -> File { + pub fn new_from_nixentry(direntry: Entry, path: &Path, dirfd: i32) -> File { // Scary stuff to avoid some of the overhead in Rusts conversions // Speedup is a solid ~10% let name: &OsStr = unsafe { @@ -1177,23 +1158,23 @@ impl File { let s: &[u8] = s.cast::<&[u8]>().as_ref().unwrap(); // &Cstr -> &OsStr, minus the NULL byte let len = s.len(); - let s = &s[..len-1] as *const [u8]; + let s = &s[..len - 1] as *const [u8]; s.cast::<&OsStr>().as_ref().unwrap() }; // Avoid reallocation on push - let mut pathstr = std::ffi::OsString::with_capacity(path.as_os_str().len() + - name.len() + - 2); + let mut pathstr = + std::ffi::OsString::with_capacity(path.as_os_str().len() + name.len() + 2); pathstr.push(path.as_os_str()); pathstr.push("/"); pathstr.push(name); let path = PathBuf::from(pathstr); - let name = name.to_str() - .map(|n| String::from(n)) - .unwrap_or_else(|| name.to_string_lossy().to_string()); + let name = name + .to_str() + .map(|n| String::from(n)) + .unwrap_or_else(|| name.to_string_lossy().to_string()); let hidden = name.as_bytes()[0] == b'.'; @@ -1202,18 +1183,16 @@ impl File { Type::Directory => (Kind::Directory, None), Type::Symlink => { // Read link target - let target = nix::fcntl::readlinkat(dirfd, &path) - .map(PathBuf::from).ok(); - let target_kind = - match path.is_dir() { - true => Kind::Directory, - false => Kind::File - }; + let target = nix::fcntl::readlinkat(dirfd, &path).map(PathBuf::from).ok(); + let target_kind = match path.is_dir() { + true => Kind::Directory, + false => Kind::File, + }; (target_kind, target) } - _ => (Kind::File, None) - } - _ => (Kind::Placeholder, None) + _ => (Kind::File, None), + }, + _ => (Kind::Placeholder, None), }; File { @@ -1247,7 +1226,11 @@ impl File { } pub fn rename(&mut self, new_path: &Path) -> HResult<()> { - self.name = new_path.file_name()?.to_string_lossy().to_string(); + self.name = new_path + .file_name() + .ok_or_else(|| HError::NoneError)? + .to_string_lossy() + .to_string(); self.path = new_path.into(); Ok(()) } @@ -1257,14 +1240,13 @@ impl File { } pub fn refresh_meta_job(&mut self) -> Job { - let meta = self.meta - .as_ref() - .map_or_else(|| Arc::default(), - |m| { - *m.write().unwrap() = None; - m.clone() - }); - + let meta = self.meta.as_ref().map_or_else( + || Arc::default(), + |m| { + *m.write().unwrap() = None; + m.clone() + }, + ); (self.path.clone(), Some(meta), None) } @@ -1283,12 +1265,12 @@ impl File { None if self.is_dir() => { let dslot = match cache.get_dirsize(self) { Some(dslot) => dslot, - None => cache.make_dirsize(self) + None => cache.make_dirsize(self), }; self.set_dirsize(dslot.clone()); Some(dslot) } - _ => None + _ => None, }; if mslot.is_some() || dslot.is_some() { @@ -1300,19 +1282,17 @@ impl File { } pub fn meta(&self) -> Option>> { - let meta = self.meta - .as_ref()? - .read() - .ok(); + let meta = self.meta.as_ref()?.read().ok(); match meta { - Some(meta) => + Some(meta) => { if meta.is_some() { Some(meta) } else { None - }, - None => None + } + } + None => None, } } @@ -1321,9 +1301,10 @@ impl File { let meta = meta.as_ref()?; match COLORS.style_for_path_with_metadata(&self.path, Some(&meta)) { // TODO: Also handle bg color, bold, etc.? - Some(style) => style.foreground - .as_ref() - .map(|c| crate::term::from_lscolor(&c)), + Some(style) => style + .foreground + .as_ref() + .map(|c| crate::term::from_lscolor(&c)), None => None, } } @@ -1338,18 +1319,17 @@ impl File { } else { return Err(FileError::MetaPending)?; } - }, + } None => (0, ""), }; return Ok(size); } - let mut unit = 0; let mut size = match self.meta() { Some(meta) => meta.as_ref().unwrap().size(), - None => return Err(FileError::MetaPending)? + None => return Err(FileError::MetaPending)?, }; while size > 1024 { size /= 1024; @@ -1374,8 +1354,8 @@ impl File { // panic with a custom panic hook and handle it gracefully by just // doing nothing pub fn get_mime(&self) -> HResult { - use std::panic; use crate::fail::MimeError; + use std::panic; if let Some(ext) = self.path.extension() { let mime = mime_guess::from_ext(&ext.to_string_lossy()).first(); @@ -1386,7 +1366,7 @@ impl File { // Take and replace panic handler which does nothing let panic_hook = panic::take_hook(); - panic::set_hook(Box::new(|_| {} )); + panic::set_hook(Box::new(|_| {})); // Catch possible panic caused by tree_magic let mime = panic::catch_unwind(|| { @@ -1397,37 +1377,36 @@ impl File { // Restore previous panic handler panic::set_hook(panic_hook); - mime.unwrap_or(None) - .ok_or_else(|| { - let file = self.name.clone(); - HError::Mime(MimeError::Panic(file)) - }) + mime.unwrap_or(None).ok_or_else(|| { + let file = self.name.clone(); + HError::Mime(MimeError::Panic(file)) + }) } - pub fn is_text(&self) -> bool { tree_magic_fork::match_filepath("text/plain", &self.path) } pub fn is_filtered(&self, filter: &str, filter_selected: bool) -> bool { - self.kind == Kind::Placeholder || - !(// filter.is_some() && - !self.name.contains(filter// .as_ref().unwrap() - )) && - (!filter_selected || self.selected) + self.kind == Kind::Placeholder + || !( + // filter.is_some() && + !self.name.contains( + filter, // .as_ref().unwrap() + ) + ) && (!filter_selected || self.selected) } pub fn is_hidden(&self) -> bool { self.hidden } - pub fn parent(&self) -> Option<&Path> { self.path.parent() } pub fn parent_as_file(&self) -> HResult { - let pathbuf = self.parent()?; + let pathbuf = self.parent().ok_or_else(|| HError::NoneError)?; File::new_from_path(&pathbuf) } @@ -1436,7 +1415,7 @@ impl File { } pub fn grand_parent_as_file(&self) -> HResult { - let pathbuf = self.grand_parent()?; + let pathbuf = self.grand_parent().ok_or_else(|| HError::NoneError)?; File::new_from_path(&pathbuf) } @@ -1456,7 +1435,7 @@ impl File { let base_path = base.path.clone(); match self.path.strip_prefix(base_path) { Ok(path) => PathBuf::from(path), - Err(_) => self.path.clone() + Err(_) => self.path.clone(), } } @@ -1483,7 +1462,7 @@ impl File { pub fn set_tag_status(&mut self, tags: &[PathBuf]) { match tags.contains(&self.path) { true => self.tag = Some(true), - false => self.tag = Some(false) + false => self.tag = Some(false), } } @@ -1502,7 +1481,9 @@ impl File { } pub fn save_tags(&self) -> HResult<()> { - if self.tag.is_none() { return Ok(()); } + if self.tag.is_none() { + return Ok(()); + } let path = self.path.clone(); let state = self.tag.unwrap(); @@ -1516,24 +1497,25 @@ impl File { match state { true => { match tags.1.binary_search(&path) { - Ok(_) => {}, - Err(inspos) => tags.1.insert(inspos, path) + Ok(_) => {} + Err(inspos) => tags.1.insert(inspos, path), }; - }, + } false => { match tags.1.binary_search(&path) { - Ok(delpos) => { tags.1.remove(delpos); }, + Ok(delpos) => { + tags.1.remove(delpos); + } Err(_) => {} }; } } - let tagstr = tags.1.iter() - .fold(std::ffi::OsString::new(), |mut s, f| { - s.push(f); - s.push("\n"); - s - }); + let tagstr = tags.1.iter().fold(std::ffi::OsString::new(), |mut s, f| { + s.push(f); + s.push("\n"); + s + }); std::fs::write(tagfile_path, tagstr.as_bytes())?; Ok(()) @@ -1542,15 +1524,23 @@ impl File { } pub fn is_readable(&self) -> HResult { - let meta = self.meta()?; - let meta = meta.as_ref()?; - let current_user = get_current_username()?.to_string_lossy().to_string(); - let current_group = get_current_groupname()?.to_string_lossy().to_string(); - let file_user = get_user_by_uid(meta.uid())? + let meta = self.meta().ok_or_else(|| HError::NoneError)?; + let meta = meta.as_ref().ok_or_else(|| HError::NoneError)?; + let current_user = get_current_username() + .ok_or_else(|| HError::NoneError)? + .to_string_lossy() + .to_string(); + let current_group = get_current_groupname() + .ok_or_else(|| HError::NoneError)? + .to_string_lossy() + .to_string(); + let file_user = get_user_by_uid(meta.uid()) + .ok_or_else(|| HError::NoneError)? .name() .to_string_lossy() .to_string(); - let file_group = get_group_by_gid(meta.gid())? + let file_group = get_group_by_gid(meta.gid()) + .ok_or_else(|| HError::NoneError)? .name() .to_string_lossy() .to_string(); @@ -1572,11 +1562,11 @@ impl File { } pub fn pretty_print_permissions(&self) -> HResult { - let meta = self.meta()?; - let meta = meta.as_ref()?; + let meta = self.meta().ok_or_else(|| HError::NoneError)?; + let meta = meta.as_ref().ok_or_else(|| HError::NoneError)?; let perms: usize = format!("{:o}", meta.mode()).parse().unwrap(); - let perms: usize = perms % 800; + let perms: usize = perms % 800; let perms = format!("{}", perms); let r = format!("{}r", crate::term::color_green()); @@ -1584,16 +1574,19 @@ impl File { let x = format!("{}x", crate::term::color_red()); let n = format!("{}-", crate::term::highlight_color()); - let perms = perms.chars().map(|c| match c.to_string().parse().unwrap() { - 1 => format!("{}{}{}", n,n,x), - 2 => format!("{}{}{}", n,w,n), - 3 => format!("{}{}{}", n,w,x), - 4 => format!("{}{}{}", r,n,n), - 5 => format!("{}{}{}", r,n,x), - 6 => format!("{}{}{}", r,w,n), - 7 => format!("{}{}{}", r,w,x), - _ => format!("---") - }).collect(); + let perms = perms + .chars() + .map(|c| match c.to_string().parse().unwrap() { + 1 => format!("{}{}{}", n, n, x), + 2 => format!("{}{}{}", n, w, n), + 3 => format!("{}{}{}", n, w, x), + 4 => format!("{}{}{}", r, n, n), + 5 => format!("{}{}{}", r, n, x), + 6 => format!("{}{}{}", r, w, n), + 7 => format!("{}{}{}", r, w, x), + _ => format!("---"), + }) + .collect(); Ok(perms) } @@ -1604,11 +1597,11 @@ impl File { let uid = meta.uid(); let file_user = users::get_user_by_uid(uid)?; let cur_user = users::get_current_username()?; - let color = - if file_user.name() == cur_user { - crate::term::color_green() - } else { - crate::term::color_red() }; + let color = if file_user.name() == cur_user { + crate::term::color_green() + } else { + crate::term::color_red() + }; Some(format!("{}{}", color, file_user.name().to_string_lossy())) } @@ -1618,11 +1611,11 @@ impl File { let gid = meta.gid(); let file_group = users::get_group_by_gid(gid)?; let cur_group = users::get_current_groupname()?; - let color = - if file_group.name() == cur_group { - crate::term::color_green() - } else { - crate::term::color_red() }; + let color = if file_group.name() == cur_group { + crate::term::color_green() + } else { + crate::term::color_red() + }; Some(format!("{}{}", color, file_group.name().to_string_lossy())) } @@ -1630,8 +1623,7 @@ impl File { let meta = self.meta()?; let meta = meta.as_ref()?; - let time: chrono::DateTime - = chrono::Local.timestamp(meta.mtime(), 0); + let time: chrono::DateTime = chrono::Local.timestamp(meta.mtime(), 0); Some(time.format("%F %R").to_string()) } @@ -1647,32 +1639,3 @@ impl File { self.path.short_string() } } - - - -// Small wrapper that simplifies stopping with more complex control flow -pub struct Ticker { - invalidated: bool -} - -impl Ticker { - pub fn start_ticking(sender: Sender) -> Self { - start_ticking(sender); - Ticker { - invalidated: false - } - } - - pub fn stop_ticking(&mut self) { - stop_ticking(); - self.invalidated = true; - } -} - -impl Drop for Ticker { - fn drop(&mut self) { - if !self.invalidated { - self.stop_ticking(); - } - } -} diff --git a/src/foldview.rs b/src/foldview.rs index b792721..03a5d0e 100644 --- a/src/foldview.rs +++ b/src/foldview.rs @@ -1,26 +1,24 @@ -use termion::event::Key; -use failure::Fail; use chrono::{DateTime, Local}; +use failure::Fail; +use termion::event::Key; +use crate::dirty::Dirtyable; +use crate::fail::{HError, HResult, KeyBindError}; +use crate::keybind::{Acting, AnyKey, BindingSection, Bindings, FoldAction, LogAction, Movement}; +use crate::listview::{ListView, Listable}; use crate::term; use crate::widget::Widget; -use crate::listview::{ListView, Listable}; -use crate::fail::{HResult, HError, KeyBindError}; -use crate::dirty::Dirtyable; -use crate::keybind::{Acting, AnyKey, Bindings, BindingSection, Movement, FoldAction, LogAction}; pub type LogView = ListView>; - #[derive(Debug)] pub struct LogEntry { description: String, content: Option, lines: usize, - folded: bool + folded: bool, } - impl Foldable for LogEntry { fn description(&self) -> &str { &self.description @@ -29,7 +27,9 @@ impl Foldable for LogEntry { self.content.as_ref() } fn lines(&self) -> usize { - if self.is_folded() { 1 } else { + if self.is_folded() { + 1 + } else { self.lines } } @@ -41,27 +41,32 @@ impl Foldable for LogEntry { } } - impl From<&HError> for LogEntry { fn from(from: &HError) -> LogEntry { let time: DateTime = Local::now(); let logcolor = match from { HError::Log(_) => term::normal_color(), - _ => term::color_red() + _ => term::color_red(), }; - let description = format!("{}{}{}: {}", - term::color_green(), - time.format("%F %R"), - logcolor, - from).lines().take(1).collect(); - let mut content = format!("{}{}{}: {}\n", - term::color_green(), - time.format("%F %R"), - logcolor, - from); - + let description = format!( + "{}{}{}: {}", + term::color_green(), + time.format("%F %R"), + logcolor, + from + ) + .lines() + .take(1) + .collect(); + let mut content = format!( + "{}{}{}: {}\n", + term::color_green(), + time.format("%F %R"), + logcolor, + from + ); if let Some(cause) = from.cause() { content += &format!("{}\n", cause); @@ -77,7 +82,7 @@ impl From<&HError> for LogEntry { description: description, content: Some(content), lines: lines, - folded: true + folded: true, } } } @@ -86,7 +91,7 @@ pub trait ActingExt where Self::Action: BindingSection + std::fmt::Debug, Bindings: Default, - Self: Widget + Self: Widget, { type Action; @@ -98,24 +103,20 @@ where let gkey = AnyKey::from(key); // Moving takes priority - if let Some(movement) = self.get_core()? - .config() - .keybinds - .movement - .get(gkey) { - match self.movement(movement) { - Ok(()) => return Ok(()), - Err(HError::KeyBind(KeyBindError::MovementUndefined)) => {} - Err(e) => Err(e)? - } + if let Some(movement) = self.get_core()?.config().keybinds.movement.get(gkey) { + match self.movement(movement) { + Ok(()) => return Ok(()), + Err(HError::KeyBind(KeyBindError::MovementUndefined)) => {} + Err(e) => Err(e)?, } + } self.search_in(); let bindings = self.search_in(); if let Some(action) = bindings.get(key) { - return self.do_action(action) + return self.do_action(action); } else if let Some(any_key) = gkey.any() { if let Some(action) = bindings.get(any_key) { let action = action.insert_key_param(key); @@ -139,7 +140,7 @@ impl ActingExt for ListView> { fn do_action(&mut self, action: &Self::Action) -> HResult<()> { match action { - LogAction::Close => self.popup_finnished() + LogAction::Close => self.popup_finnished(), } } } @@ -147,18 +148,26 @@ impl ActingExt for ListView> { pub trait FoldableWidgetExt where Self: ActingExt, - Bindings<::Action>: Default + Bindings<::Action>: Default, { - fn on_refresh(&mut self) -> HResult<()> { Ok(()) } - fn render_header(&self) -> HResult { Ok("".to_string()) } - fn render_footer(&self) -> HResult { Ok("".to_string()) } + fn on_refresh(&mut self) -> HResult<()> { + Ok(()) + } + fn render_header(&self) -> HResult { + Ok("".to_string()) + } + fn render_footer(&self) -> HResult { + Ok("".to_string()) + } fn on_key(&mut self, key: Key) -> HResult<()> { HError::undefined_key(key)? } - fn render(&self) -> Vec { vec![] } + fn render(&self) -> Vec { + vec![] + } } -impl FoldableWidgetExt for ListView> { +impl FoldableWidgetExt for ListView> { fn on_refresh(&mut self) -> HResult<()> { if self.content.refresh_logs()? > 0 { self.core.set_dirty(); @@ -168,19 +177,21 @@ impl FoldableWidgetExt for ListView> { fn render_header(&self) -> HResult { let (xsize, _) = self.core.coordinates.size_u(); - let current = self.current_fold().map(|n| n+1).unwrap_or(0); + let current = self.current_fold().map(|n| n + 1).unwrap_or(0); let num = self.content.len(); let hint = format!("{} / {}", current, num); let hint_xpos = xsize - hint.len(); - let header = format!("Logged entries: {}{}{}", - num, - term::goto_xy_u(hint_xpos, 0), - hint); + let header = format!( + "Logged entries: {}{}{}", + num, + term::goto_xy_u(hint_xpos, 0), + hint + ); Ok(header) } fn render_footer(&self) -> HResult { - let current = self.current_fold()?; + let current = self.current_fold().ok_or_else(|| HError::NoneError)?; if let Some(logentry) = self.content.get(current) { let (xsize, ysize) = self.core.coordinates.size_u(); let (_, ypos) = self.core.coordinates.position_u(); @@ -193,19 +204,22 @@ impl FoldableWidgetExt for ListView> { let hint_xpos = xsize - line_hint.len(); let hint_ypos = ysize + ypos + 1; - let sized_description = term::sized_string_u(&description, - xsize - - (line_hint.len()+2)); + let sized_description = + term::sized_string_u(&description, xsize - (line_hint.len() + 2)); - let footer = format!("{}{}{}{}{}", - sized_description, - term::reset(), - term::status_bg(), - term::goto_xy_u(hint_xpos, hint_ypos), - line_hint); + let footer = format!( + "{}{}{}{}{}", + sized_description, + term::reset(), + term::status_bg(), + term::goto_xy_u(hint_xpos, hint_ypos), + line_hint + ); Ok(footer) - } else { Ok("No log entries".to_string()) } + } else { + Ok("No log entries".to_string()) + } } } @@ -217,9 +231,10 @@ impl LogList for Vec { fn refresh_logs(&mut self) -> HResult { let logs = crate::fail::get_logs()?; - let mut logentries = logs.into_iter().map(|log| { - LogEntry::from(log) - }).collect::>(); + let mut logentries = logs + .into_iter() + .map(|log| LogEntry::from(log)) + .collect::>(); let n = logentries.len(); @@ -229,7 +244,6 @@ impl LogList for Vec { } } - pub trait Foldable { fn description(&self) -> &str; fn content(&self) -> Option<&String>; @@ -251,11 +265,10 @@ pub trait Foldable { fn render_content(&self) -> Vec { if let Some(content) = self.content() { - content - .lines() - .map(|line| line.to_string()) - .collect() - } else { vec![self.render_description()] } + content.lines().map(|line| line.to_string()).collect() + } else { + vec![self.render_description()] + } } fn render(&self) -> Vec { @@ -270,10 +283,10 @@ pub trait Foldable { impl ListView> where ListView>: FoldableWidgetExt, - Bindings<> as ActingExt>::Action>: Default { - + Bindings<> as ActingExt>::Action>: Default, +{ pub fn toggle_fold(&mut self) -> HResult<()> { - let fold = self.current_fold()?; + let fold = self.current_fold().ok_or_else(|| HError::NoneError)?; let fold_pos = self.fold_start_pos(fold); self.content[fold].toggle_fold(); @@ -290,9 +303,7 @@ where self.content .iter() .take(fold) - .fold(0, |pos, foldable| { - pos + (foldable.lines()) - }) + .fold(0, |pos, foldable| pos + (foldable.lines())) } pub fn current_fold(&self) -> Option { @@ -316,15 +327,16 @@ where } else { (lines + current_fold_lines, None) } - }}).1 + } + }) + .1 } } - impl Listable for ListView> where ListView>: FoldableWidgetExt, - Bindings<> as ActingExt>::Action>: Default + Bindings<> as ActingExt>::Action>: Default, { type Item = (); @@ -335,17 +347,20 @@ where fn render(&self) -> Vec { let rendering = FoldableWidgetExt::render(self); // HACK to check if no custom renderer - if rendering.len() > 0 { return rendering; } + if rendering.len() > 0 { + return rendering; + } let (xsize, _) = self.core.coordinates.size_u(); self.content .iter() - .map(|foldable| - foldable - .render() - .iter() - .map(|line| term::sized_string_u(line, xsize)) - .collect::>()) + .map(|foldable| { + foldable + .render() + .iter() + .map(|line| term::sized_string_u(line, xsize)) + .collect::>() + }) .flatten() .collect() } @@ -365,7 +380,7 @@ where fn on_key(&mut self, key: Key) -> HResult<()> { match ActingExt::do_key_ext(self, key) { Err(HError::PopupFinnished) => Err(HError::PopupFinnished), - _ => self.do_key(key) + _ => self.do_key(key), } } } @@ -373,7 +388,7 @@ where impl Acting for ListView> where ListView>: FoldableWidgetExt, - Bindings<> as ActingExt>::Action>: Default + Bindings<> as ActingExt>::Action>: Default, { type Action = FoldAction; @@ -384,11 +399,18 @@ where fn movement(&mut self, movement: &Movement) -> HResult<()> { use Movement::*; - match movement { - Up(n) => for _ in 0..*n { self.move_up() }, - Down(n) => for _ in 0..*n { self.move_down() }, - _ => { Err(KeyBindError::MovementUndefined)? }, + Up(n) => { + for _ in 0..*n { + self.move_up() + } + } + Down(n) => { + for _ in 0..*n { + self.move_down() + } + } + _ => Err(KeyBindError::MovementUndefined)?, } Ok(()) @@ -398,7 +420,7 @@ where use FoldAction::*; match action { - ToggleFold => self.toggle_fold() + ToggleFold => self.toggle_fold(), } } } diff --git a/src/fscache.rs b/src/fscache.rs index 11d30f9..9877544 100644 --- a/src/fscache.rs +++ b/src/fscache.rs @@ -1,20 +1,19 @@ -use notify::{RecommendedWatcher, Watcher, DebouncedEvent, RecursiveMode}; +use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; use async_value::{Async, Stale}; -use std::sync::{Arc, RwLock, Weak}; -use std::sync::mpsc::{channel, Sender, Receiver}; use std::collections::{HashMap, HashSet}; -use std::time::Duration; use std::path::{Path, PathBuf}; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::{Arc, RwLock, Weak}; +use std::time::Duration; -use crate::files::{Files, File, SortBy}; +use crate::fail::{ErrorLog, HError, HResult}; +use crate::files::{File, Files, SortBy}; use crate::widget::Events; -use crate::fail::{HResult, HError, ErrorLog}; pub type CachedFiles = (Option, Async); - #[derive(Debug, Clone)] pub struct DirSettings { sort: SortBy, @@ -22,7 +21,7 @@ pub struct DirSettings { reverse: bool, show_hidden: bool, filter: Option, - filter_selected: bool + filter_selected: bool, } impl DirSettings { @@ -33,7 +32,7 @@ impl DirSettings { reverse: false, show_hidden: true, filter: None, - filter_selected: false + filter_selected: false, } } } @@ -50,73 +49,65 @@ impl TabSettings { TabSettings { selection: None, multi_selections: vec![], - dir_settings: DirSettings::new() + dir_settings: DirSettings::new(), } } } - impl std::fmt::Debug for FsCache { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, - "{:?}\n{:?}\n{:?}", - self.tab_settings, - self.watched_dirs, - self.files) + write!( + formatter, + "{:?}\n{:?}\n{:?}", + self.tab_settings, self.watched_dirs, self.files + ) } } #[derive(Clone)] struct FsEventDispatcher { - targets: Arc>>>>>> + targets: Arc>>>>>>, } impl FsEventDispatcher { fn new() -> Self { FsEventDispatcher { - targets: Arc::new(RwLock::new(HashMap::new())) + targets: Arc::new(RwLock::new(HashMap::new())), } } - fn add_target(&self, - dir: &File, - target: &Arc>>) -> HResult<()> { + fn add_target(&self, dir: &File, target: &Arc>>) -> HResult<()> { let target = Arc::downgrade(target); self.targets .write() - .map(|mut targets| { - match targets.get_mut(dir) { - Some(targets) => targets.push(target), - None => { targets.insert(dir.clone(), vec![target]); } + .map(|mut targets| match targets.get_mut(dir) { + Some(targets) => targets.push(target), + None => { + targets.insert(dir.clone(), vec![target]); } })?; Ok(()) } fn remove_target(&self, dir: &File) -> HResult<()> { - self.targets - .write()? - .get_mut(dir) - .map(|targets| { - targets.retain(|t| t.upgrade().is_some()); - }); + self.targets.write()?.get_mut(dir).map(|targets| { + targets.retain(|t| t.upgrade().is_some()); + }); Ok(()) } fn dispatch(&self, events: HashMap>) -> HResult<()> { for (dir, events) in events { - for target_dirs in self.targets - .read()? - .get(&dir) { - for target in target_dirs { - if let Some(target) = target.upgrade() { - let events = events.clone(); - - target.write()?.extend(events) - } + for target_dirs in self.targets.read()?.get(&dir) { + for target in target_dirs { + if let Some(target) = target.upgrade() { + let events = events.clone(); + + target.write()?.extend(events) } } + } } Ok(()) } @@ -127,20 +118,17 @@ use std::sync::atomic::{AtomicBool, AtomicUsize}; #[derive(Clone)] pub struct FsCache { files: Arc>>, - dirsizes: Arc>>>>, + dirsizes: Arc>>>>, pub tab_settings: Arc>>, watched_dirs: Arc>>, watcher: Arc>, - fs_event_dispatcher: FsEventDispatcher + fs_event_dispatcher: FsEventDispatcher, } impl FsCache { pub fn new(sender: Sender) -> FsCache { let (tx_fs_event, rx_fs_event) = channel(); - let watcher = RecommendedWatcher::new(tx_fs_event, - Duration::from_secs(2)).unwrap(); - + let watcher = RecommendedWatcher::new(tx_fs_event, Duration::from_secs(2)).unwrap(); let fs_cache = FsCache { files: Arc::new(RwLock::new(HashMap::new())), @@ -148,12 +136,10 @@ impl FsCache { tab_settings: Arc::new(RwLock::new(HashMap::new())), watched_dirs: Arc::new(RwLock::new(HashSet::new())), watcher: Arc::new(RwLock::new(watcher)), - fs_event_dispatcher: FsEventDispatcher::new() + fs_event_dispatcher: FsEventDispatcher::new(), }; - watch_fs(rx_fs_event, - fs_cache.fs_event_dispatcher.clone(), - sender); + watch_fs(rx_fs_event, fs_cache.fs_event_dispatcher.clone(), sender); fs_cache } @@ -176,8 +162,10 @@ impl FsCache { let files = Async::new(move |_| { let mut files = Files::new_from_path_cancellable(&dir.path, stale)?; cache.add_watch(&dir).log(); - cache.fs_event_dispatcher.add_target(&dir, - &files.pending_events).log(); + cache + .fs_event_dispatcher + .add_target(&dir, &files.pending_events) + .log(); FsCache::apply_settingss(&cache, &mut files).ok(); Ok(files) }); @@ -200,21 +188,23 @@ impl FsCache { } pub fn get_selection(&self, dir: &File) -> HResult { - Ok(self.tab_settings - .read()? - .get(&dir) - .as_ref()? - .selection - .as_ref()? - .clone()) + Ok(self + .tab_settings + .read()? + .get(&dir) + .as_ref() + .ok_or_else(|| HError::NoneError)? + .selection + .as_ref() + .ok_or_else(|| HError::NoneError)? + .clone()) } pub fn set_selection(&self, dir: File, selection: File) -> HResult<()> { - self.tab_settings.write() - .map(|mut settings| { - let setting = settings.entry(dir).or_insert(TabSettings::new()); - setting.selection = Some(selection); - })?; + self.tab_settings.write().map(|mut settings| { + let setting = settings.entry(dir).or_insert(TabSettings::new()); + setting.selection = Some(selection); + })?; Ok(()) } @@ -230,28 +220,22 @@ impl FsCache { } pub fn get_dirsize(&self, dir: &File) -> Option> { - let parent_dir = dir.parent() - .unwrap_or_else(|| Path::new("/")); + let parent_dir = dir.parent().unwrap_or_else(|| Path::new("/")); self.dirsizes .read() .unwrap() .get(parent_dir) - .map(|parent_map| { - parent_map.get(&dir.path) - }) + .map(|parent_map| parent_map.get(&dir.path)) .flatten() .cloned() } pub fn make_dirsize(&self, dir: &File) -> Arc<(AtomicBool, AtomicUsize)> { - let parent_dir = dir.parent() - .unwrap_or_else(|| Path::new("/")); + let parent_dir = dir.parent().unwrap_or_else(|| Path::new("/")); let dir = dir.path.as_path(); - let mut dirsizes = self.dirsizes - .write() - .unwrap(); + let mut dirsizes = self.dirsizes.write().unwrap(); match dirsizes.contains_key(parent_dir) { false => { @@ -259,16 +243,10 @@ impl FsCache { let ready = AtomicBool::new(false); let size = AtomicUsize::default(); let dirsize = Arc::new((ready, size)); - dir_map.insert(dir.to_path_buf(), - dirsize); - dirsizes.insert(parent_dir.to_path_buf(), - dir_map); - - return dirsizes.get(parent_dir) - .unwrap() - .get(dir) - .unwrap() - .clone(); + dir_map.insert(dir.to_path_buf(), dirsize); + dirsizes.insert(parent_dir.to_path_buf(), dir_map); + + return dirsizes.get(parent_dir).unwrap().get(dir).unwrap().clone(); } true => { let pmap = dirsizes.get_mut(parent_dir).unwrap(); @@ -281,9 +259,7 @@ impl FsCache { let dirsize = Arc::new((ready, size)); pmap.insert(dir.to_path_buf(), dirsize); - return pmap.get(dir) - .unwrap() - .clone() + return pmap.get(dir).unwrap().clone(); } } } @@ -291,7 +267,8 @@ impl FsCache { } pub fn watch_only(&self, open_dirs: HashSet) -> HResult<()> { - let removable = self.watched_dirs + let removable = self + .watched_dirs .read()? .difference(&open_dirs) .map(|dir| dir.clone()) @@ -306,7 +283,9 @@ impl FsCache { fn add_watch(&self, dir: &File) -> HResult<()> { if !self.watched_dirs.read()?.contains(&dir) { - self.watcher.write()?.watch(&dir.path, RecursiveMode::NonRecursive)?; + self.watcher + .write()? + .watch(&dir.path, RecursiveMode::NonRecursive)?; self.watched_dirs.write()?.insert(dir.clone()); } Ok(()) @@ -322,15 +301,16 @@ impl FsCache { fn get_cached_files(&self, dir: &File) -> HResult { let tab_settings = match self.tab_settings.read()?.get(&dir) { - Some(tab_settings) => tab_settings.clone(), - None => TabSettings::new() + Some(tab_settings) => tab_settings.clone(), + None => TabSettings::new(), }; let selection = tab_settings.selection.clone(); let file_cache = self.files.clone(); let dir = dir.clone(); let files = Async::new(move |_| { - let mut files = file_cache.read() + let mut files = file_cache + .read() .map_err(|e| HError::from(e))? .get(&dir) .ok_or(HError::NoneError)? @@ -360,20 +340,20 @@ impl FsCache { Ok((selection, files)) } - - pub fn apply_settingss(cache: &FsCache, - files: &mut Files) - -> HResult<()> { + pub fn apply_settingss(cache: &FsCache, files: &mut Files) -> HResult<()> { let dir = &files.directory; let tab_settings = cache.tab_settings.read()?.get(&dir).cloned(); - if tab_settings.is_none() { return Ok(()) } - let tab_settings = tab_settings?; + if tab_settings.is_none() { + return Ok(()); + } + let tab_settings = tab_settings.ok_or_else(|| HError::NoneError)?; - if files.show_hidden != tab_settings.dir_settings.show_hidden || - files.filter != tab_settings.dir_settings.filter || - files.filter_selected != tab_settings.dir_settings.filter_selected { - files.recalculate_len(); - } + if files.show_hidden != tab_settings.dir_settings.show_hidden + || files.filter != tab_settings.dir_settings.filter + || files.filter_selected != tab_settings.dir_settings.filter_selected + { + files.recalculate_len(); + } files.sort = tab_settings.dir_settings.sort; files.dirs_first = tab_settings.dir_settings.dirs_first; @@ -382,8 +362,6 @@ impl FsCache { files.filter = tab_settings.dir_settings.filter.clone(); files.filter_selected = tab_settings.dir_settings.filter_selected; - - if tab_settings.multi_selections.len() > 0 { for file in &mut files.files { for selected_files in &tab_settings.multi_selections { @@ -407,7 +385,6 @@ impl FsCache { Ok(files) } - fn extract_tab_settings(files: &Files, selection: Option) -> TabSettings { TabSettings { selection: selection, @@ -418,39 +395,38 @@ impl FsCache { reverse: files.reverse, show_hidden: files.show_hidden, filter: files.filter.clone(), - filter_selected: files.filter_selected - } + filter_selected: files.filter_selected, + }, } } } - #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub enum FsEvent { Create(File), Change(File), Rename(File, File), - Remove(File) + Remove(File), } impl FsEvent { pub fn file(&self) -> &File { use FsEvent::*; match self { - Create(event_file) | - Change(event_file) | - Remove(event_file) | - Rename(_, event_file) => &event_file + Create(event_file) + | Change(event_file) + | Remove(event_file) + | Rename(_, event_file) => &event_file, } } pub fn for_file(&self, file: &File) -> bool { use FsEvent::*; match self { - Create(event_file) | - Change(event_file) | - Remove(event_file) | - Rename(_, event_file) => event_file.path == file.path + Create(event_file) + | Change(event_file) + | Remove(event_file) + | Rename(_, event_file) => event_file.path == file.path, } } } @@ -461,86 +437,82 @@ impl TryFrom for FsEvent { fn try_from(event: DebouncedEvent) -> HResult { let event = match event { - DebouncedEvent::Create(path) - => FsEvent::Create(File::new_from_path(&path)?), + DebouncedEvent::Create(path) => FsEvent::Create(File::new_from_path(&path)?), - DebouncedEvent::Remove(path) - => FsEvent::Remove(File::new_from_path(&path)?), + DebouncedEvent::Remove(path) => FsEvent::Remove(File::new_from_path(&path)?), - DebouncedEvent::Write(path) | - DebouncedEvent::Chmod(path) - => FsEvent::Change(File::new_from_path(&path)?), + DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => { + FsEvent::Change(File::new_from_path(&path)?) + } - DebouncedEvent::Rename(old_path, new_path) - => FsEvent::Rename(File::new_from_path(&old_path)?, - File::new_from_path(&new_path)?), + DebouncedEvent::Rename(old_path, new_path) => FsEvent::Rename( + File::new_from_path(&old_path)?, + File::new_from_path(&new_path)?, + ), - DebouncedEvent::Error(err, path) - => Err(HError::INotifyError(format!("{}, {:?}", err, path)))?, - DebouncedEvent::Rescan - => Err(HError::INotifyError("Need to rescan".to_string()))?, + DebouncedEvent::Error(err, path) => { + Err(HError::INotifyError(format!("{}, {:?}", err, path)))? + } + DebouncedEvent::Rescan => Err(HError::INotifyError("Need to rescan".to_string()))?, // Ignore NoticeRemove/NoticeWrite - _ => None?, + _ => None.ok_or_else(|| HError::NoneError)?, }; Ok(event) } } - -fn watch_fs(rx_fs_events: Receiver, - fs_event_dispatcher: FsEventDispatcher, - sender: Sender) { +fn watch_fs( + rx_fs_events: Receiver, + fs_event_dispatcher: FsEventDispatcher, + sender: Sender, +) { std::thread::spawn(move || -> HResult<()> { - let transform_event = - move |event: DebouncedEvent| -> HResult<(File, FsEvent)> { - let path = event.get_source_path()?; - let dirpath = path.parent() - .map(|path| path) - .unwrap_or(std::path::Path::new("/")); - let dir = File::new_from_path(&dirpath)?; - let event = FsEvent::try_from(event)?; - Ok((dir, event)) - }; - - let collect_events = - move || -> HResult>> { - let event = loop { - use DebouncedEvent::*; + let transform_event = move |event: DebouncedEvent| -> HResult<(File, FsEvent)> { + let path = event.get_source_path()?; + let dirpath = path + .parent() + .map(|path| path) + .unwrap_or(std::path::Path::new("/")); + let dir = File::new_from_path(&dirpath)?; + let event = FsEvent::try_from(event)?; + Ok((dir, event)) + }; - let event = rx_fs_events.recv()?; - match event { - NoticeWrite(_) => continue, - NoticeRemove(_) => continue, - _ => break std::iter::once(event) - } - }; + let collect_events = move || -> HResult>> { + let event = loop { + use DebouncedEvent::*; - // Wait a bit to batch up more events - std::thread::sleep(std::time::Duration::from_millis(100)); + let event = rx_fs_events.recv()?; + match event { + NoticeWrite(_) => continue, + NoticeRemove(_) => continue, + _ => break std::iter::once(event), + } + }; - // Batch up all other remaining events received so far - let events = event.chain(rx_fs_events.try_iter()) - .map(transform_event) - .flatten() - .fold(HashMap::with_capacity(1000), |mut events, (dir, event)| { - events.entry(dir) - .or_insert(vec![]) - .push(event); + // Wait a bit to batch up more events + std::thread::sleep(std::time::Duration::from_millis(100)); - events - }); + // Batch up all other remaining events received so far + let events = event + .chain(rx_fs_events.try_iter()) + .map(transform_event) + .flatten() + .fold(HashMap::with_capacity(1000), |mut events, (dir, event)| { + events.entry(dir).or_insert(vec![]).push(event); - Ok(events) - }; + events + }); + Ok(events) + }; - let dispatch_events = - move |events| -> HResult<()> { - fs_event_dispatcher.dispatch(events)?; - sender.send(Events::WidgetReady)?; - Ok(()) - }; + let dispatch_events = move |events| -> HResult<()> { + fs_event_dispatcher.dispatch(events)?; + sender.send(Events::WidgetReady)?; + Ok(()) + }; loop { if let Ok(events) = collect_events().log_and() { @@ -550,8 +522,6 @@ fn watch_fs(rx_fs_events: Receiver, }); } - - trait PathFromEvent { fn get_source_path(&self) -> HResult<&PathBuf>; } @@ -559,18 +529,17 @@ trait PathFromEvent { impl PathFromEvent for DebouncedEvent { fn get_source_path(&self) -> HResult<&PathBuf> { match self { - DebouncedEvent::Create(path) | - DebouncedEvent::Write(path) | - DebouncedEvent::Chmod(path) | - DebouncedEvent::Remove(path) | - DebouncedEvent::NoticeWrite(path) | - DebouncedEvent::NoticeRemove(path) => Ok(path), + DebouncedEvent::Create(path) + | DebouncedEvent::Write(path) + | DebouncedEvent::Chmod(path) + | DebouncedEvent::Remove(path) + | DebouncedEvent::NoticeWrite(path) + | DebouncedEvent::NoticeRemove(path) => Ok(path), DebouncedEvent::Rename(old_path, _) => Ok(old_path), - DebouncedEvent::Error(err, path) - => Err(HError::INotifyError(format!("{}, {:?}", err, path))), - DebouncedEvent::Rescan - => Err(HError::INotifyError("Need to rescan".to_string())) - + DebouncedEvent::Error(err, path) => { + Err(HError::INotifyError(format!("{}, {:?}", err, path))) + } + DebouncedEvent::Rescan => Err(HError::INotifyError("Need to rescan".to_string())), } } } diff --git a/src/hbox.rs b/src/hbox.rs index 391aae0..9c6fa26 100644 --- a/src/hbox.rs +++ b/src/hbox.rs @@ -1,8 +1,8 @@ -use termion::event::{Event}; +use termion::event::Event; +use crate::coordinates::{Coordinates, Position, Size}; +use crate::fail::{ErrorLog, HError, HResult}; use crate::widget::{Widget, WidgetCore}; -use crate::coordinates::{Coordinates, Size, Position}; -use crate::fail::{HResult, HError, ErrorLog}; #[derive(Debug, PartialEq)] pub struct HBox { @@ -13,31 +13,37 @@ pub struct HBox { pub active: Option, } - -impl HBox where T: Widget + PartialEq { +impl HBox +where + T: Widget + PartialEq, +{ pub fn new(core: &WidgetCore) -> HBox { - HBox { core: core.clone(), - widgets: vec![], - ratios: None, - zoom_active: false, - active: None - } + HBox { + core: core.clone(), + widgets: vec![], + ratios: None, + zoom_active: false, + active: None, + } } - pub fn resize_children(&mut self) -> HResult<()> { let len = self.widgets.len(); - if len == 0 { return Ok(()) } + if len == 0 { + return Ok(()); + } if self.zoom_active { let coords = self.core.coordinates.clone(); - self.active_widget_mut()?.set_coordinates(&coords).log(); + self.active_widget_mut() + .ok_or_else(|| HError::NoneError)? + .set_coordinates(&coords) + .log(); return Ok(()); } let coords: Vec = self.calculate_coordinates()?; - for (widget, coord) in self.widgets.iter_mut().zip(coords.iter()) { widget.set_coordinates(coord).log(); } @@ -83,14 +89,15 @@ impl HBox where T: Widget + PartialEq { pub fn calculate_equal_ratios(&self) -> HResult> { let len = self.widgets.len(); - if len == 0 { return HError::no_widget(); } + if len == 0 { + return HError::no_widget(); + } let ratios = (0..len).map(|_| 100 / len).collect(); Ok(ratios) } - pub fn calculate_coordinates(&self) - -> HResult> { + pub fn calculate_coordinates(&self) -> HResult> { let box_coords = self.get_coordinates()?; let box_xsize = box_coords.xsize(); let box_ysize = box_coords.ysize(); @@ -98,56 +105,62 @@ impl HBox where T: Widget + PartialEq { let mut ratios = match &self.ratios { Some(ratios) => ratios.clone(), - None => self.calculate_equal_ratios()? + None => self.calculate_equal_ratios()?, }; let mut ratios_sum: usize = ratios.iter().sum(); - ratios = ratios.iter().map(|&r| - (r as f64 * box_xsize as f64 / ratios_sum as f64).round() as usize).collect(); + ratios = ratios + .iter() + .map(|&r| (r as f64 * box_xsize as f64 / ratios_sum as f64).round() as usize) + .collect(); for r in &mut ratios { - if *r < 10 { *r = 10 } + if *r < 10 { + *r = 10 + } } ratios_sum = ratios.iter().sum(); while ratios_sum + ratios.len() > box_xsize as usize { - let ratios_max = ratios.iter() - .position(|&r| r == *ratios.iter().max().unwrap()).unwrap(); + let ratios_max = ratios + .iter() + .position(|&r| r == *ratios.iter().max().unwrap()) + .unwrap(); ratios[ratios_max] = ratios[ratios_max] - 1; ratios_sum -= 1; } - let coords = ratios.iter().fold(Vec::::new(), |mut coords, ratio| { - let len = coords.len(); - let gap = if len == ratios.len() { 0 } else { 1 }; - - let widget_xsize = *ratio as u16; - let widget_xpos = if len == 0 { - box_coords.top().x() - } else { - let prev_coords = coords.last().unwrap(); - let prev_xsize = prev_coords.xsize(); - let prev_xpos = prev_coords.position().x(); - - prev_xsize + prev_xpos + gap - }; - - coords.push(Coordinates { - size: Size((widget_xsize, - box_ysize)), - position: Position((widget_xpos, - box_top)) + let coords = ratios + .iter() + .fold(Vec::::new(), |mut coords, ratio| { + let len = coords.len(); + let gap = if len == ratios.len() { 0 } else { 1 }; + + let widget_xsize = *ratio as u16; + let widget_xpos = if len == 0 { + box_coords.top().x() + } else { + let prev_coords = coords.last().unwrap(); + let prev_xsize = prev_coords.xsize(); + let prev_xpos = prev_coords.position().x(); + + prev_xsize + prev_xpos + gap + }; + + coords.push(Coordinates { + size: Size((widget_xsize, box_ysize)), + position: Position((widget_xpos, box_top)), + }); + coords }); - coords - }); Ok(coords) } pub fn set_active(&mut self, i: usize) -> HResult<()> { - if i+1 > self.widgets.len() { + if i + 1 > self.widgets.len() { HError::no_widget()? } self.active = Some(i); @@ -163,10 +176,10 @@ impl HBox where T: Widget + PartialEq { } } - - - -impl Widget for HBox where T: Widget + PartialEq { +impl Widget for HBox +where + T: Widget + PartialEq, +{ fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } @@ -180,12 +193,17 @@ impl Widget for HBox where T: Widget + PartialEq { } fn render_header(&self) -> HResult { - self.active_widget()?.render_header() + self.active_widget() + .ok_or_else(|| HError::NoneError)? + .render_header() } fn refresh(&mut self) -> HResult<()> { if self.zoom_active { - self.active_widget_mut()?.refresh().log(); + self.active_widget_mut() + .ok_or_else(|| HError::NoneError)? + .refresh() + .log(); return Ok(()); } @@ -198,20 +216,34 @@ impl Widget for HBox where T: Widget + PartialEq { fn get_drawlist(&self) -> HResult { if self.zoom_active { - return self.active_widget()?.get_drawlist(); + return self + .active_widget() + .ok_or_else(|| HError::NoneError)? + .get_drawlist(); } - Ok(self.widgets.iter().map(|child| { - child.get_drawlist().log_and().unwrap_or_else(|_| String::new()) - }).collect()) + Ok(self + .widgets + .iter() + .map(|child| { + child + .get_drawlist() + .log_and() + .unwrap_or_else(|_| String::new()) + }) + .collect()) } fn on_event(&mut self, event: Event) -> HResult<()> { - self.active_widget_mut()?.on_event(event)?; + self.active_widget_mut() + .ok_or_else(|| HError::NoneError)? + .on_event(event)?; Ok(()) } fn on_key(&mut self, key: termion::event::Key) -> HResult<()> { - self.active_widget_mut()?.on_key(key) + self.active_widget_mut() + .ok_or_else(|| HError::NoneError)? + .on_key(key) } } diff --git a/src/hunter-media.rs b/src/hunter-media.rs index 82e628d..a0a9be6 100644 --- a/src/hunter-media.rs +++ b/src/hunter-media.rs @@ -1,20 +1,19 @@ // Based on https://github.com/jD91mZM2/termplay // MIT License -use image::{RgbaImage, DynamicImage, GenericImageView}; use base64; +use image::{DynamicImage, GenericImageView, RgbaImage}; use termion::color::{Bg, Fg, Rgb}; #[cfg(feature = "video")] use termion::input::TermRead; - #[cfg(feature = "video")] use gstreamer::prelude::*; #[cfg(feature = "video")] use gstreamer_app; -use failure::{Error, format_err}; +use failure::{format_err, Error}; use std::io::Write; #[cfg(feature = "video")] @@ -24,41 +23,38 @@ pub type MResult = Result; fn main() -> MResult<()> { let args = std::env::args().collect::>(); - let xsize: usize = args.get(1) + let xsize: usize = args + .get(1) .expect("Provide xsize") .parse::() .unwrap(); - let ysize = args.get(2) - .expect("provide ysize") - .parse() - .unwrap(); - let mut xpix = args.get(3) + let ysize = args.get(2).expect("provide ysize").parse().unwrap(); + let mut xpix = args + .get(3) .expect("provide xsize in pixels") .parse::() .unwrap(); - let mut ypix = args.get(4) + let mut ypix = args + .get(4) .expect("provide ysize in pixels") .parse::() .unwrap(); - let cell_ratio = args.get(5) + let cell_ratio = args + .get(5) .expect("Provide cell ratio") .parse::() .unwrap(); - let preview_type = args.get(6) + let preview_type = args + .get(6) .expect("Provide preview type") .parse::() .unwrap(); #[allow(unused_variables)] - let autoplay = args.get(7) - .expect("Autoplay?") - .parse::() - .unwrap(); + let autoplay = args.get(7).expect("Autoplay?").parse::().unwrap(); #[allow(unused_variables)] - let mute = args.get(8) - .expect("Muted?") - .parse::() - .unwrap(); - let target = args.get(9) + let mute = args.get(8).expect("Muted?").parse::().unwrap(); + let target = args + .get(9) .expect("Render target?") .parse::() .unwrap(); @@ -77,7 +73,7 @@ fn main() -> MResult<()> { _ => RenderTarget::Unicode, } } - _ => RenderTarget::Unicode + _ => RenderTarget::Unicode, }; if target == RenderTarget::Unicode { @@ -85,37 +81,23 @@ fn main() -> MResult<()> { ypix = ysize * 2; } + let renderer = Renderer::new(target, xsize, ysize, xpix, ypix, cell_ratio); + let result = match preview_type.as_ref() { + #[cfg(feature = "video")] + "video" => video_preview(path, renderer, autoplay, mute), - let renderer = Renderer::new(target, - xsize, - ysize, - xpix, - ypix, - cell_ratio); + "image" => image_preview(path, renderer), - let result = - match preview_type.as_ref() { - #[cfg(feature = "video")] - "video" => video_preview(path, - renderer, - autoplay, - mute), + #[cfg(feature = "video")] + "audio" => audio_preview(path, autoplay, mute), - "image" => image_preview(path, - renderer), + #[cfg(feature = "video")] + _ => panic!("Available types: video/image/audio"), - #[cfg(feature = "video")] - "audio" => audio_preview(path, - autoplay, - mute), - - #[cfg(feature = "video")] - _ => { panic!("Available types: video/image/audio") } - - #[cfg(not(feature = "video"))] - _ => { panic!("Available type: image") } - }; + #[cfg(not(feature = "video"))] + _ => panic!("Available type: image"), + }; if result.is_err() { println!("{:?}", &result); @@ -125,14 +107,16 @@ fn main() -> MResult<()> { } } -fn image_preview(path: &str, - renderer: Renderer) -> MResult<()> { +fn image_preview(path: &str, renderer: Renderer) -> MResult<()> { let img = image::open(&path)?; let max_size = renderer.max_size_pix(&img); - let img = img.resize_exact(max_size.0 as u32, - max_size.1 as u32, - image::FilterType::Gaussian) + let img = img + .resize_exact( + max_size.0 as u32, + max_size.1 as u32, + image::FilterType::Gaussian, + ) .to_rgba(); renderer.send_image(&img)?; @@ -173,13 +157,8 @@ impl ImgSize for DynamicImage { } } - #[cfg(feature = "video")] -fn video_preview(path: &String, - renderer: Renderer, - autoplay: bool, - mute: bool) - -> MResult<()> { +fn video_preview(path: &String, renderer: Renderer, autoplay: bool, mute: bool) -> MResult<()> { let gst = Gstreamer::new(path)?; let renderer = Arc::new(RwLock::new(renderer)); @@ -196,7 +175,7 @@ fn video_preview(path: &String, let sample = match sink.pull_sample() { Some(sample) => sample, - None => return Err(gstreamer::FlowError::Eos) + None => return Err(gstreamer::FlowError::Eos), }; let pos = gst.position(); @@ -205,15 +184,15 @@ fn video_preview(path: &String, std::thread::spawn(move || { // This will lock make sure only one frame is being sent // at a time - renderer.try_write() - .map(|mut r| r.new_frame(sample, - pos, - dur).unwrap()) + renderer + .try_write() + .map(|mut r| r.new_frame(sample, pos, dur).unwrap()) .map_err(|_| { // But if processing takes too long, reduce rate let rate = gst.get_rate().unwrap(); - gst.set_rate(rate-1) - }).ok(); + gst.set_rate(rate - 1) + }) + .ok(); }); Ok(gstreamer::FlowSuccess::Ok) @@ -224,7 +203,7 @@ fn video_preview(path: &String, std::process::exit(0); } }) - .build() + .build(), ); // Flush pipeline and restart with corrent resizing @@ -243,16 +222,12 @@ fn video_preview(path: &String, } #[cfg(feature = "video")] -fn read_keys(gst: Gstreamer, - renderer: Option>>) -> MResult<()> { +fn read_keys(gst: Gstreamer, renderer: Option>>) -> MResult<()> { let stdin = std::io::stdin(); let mut stdin = stdin.lock(); loop { - let input = stdin - .read_line()? - .unwrap_or_else(|| String::from("q")); - + let input = stdin.read_line()?.unwrap_or_else(|| String::from("q")); match input.as_str() { "q" => return gst.stop(), @@ -263,7 +238,7 @@ fn read_keys(gst: Gstreamer, gst.send_preroll(&r).unwrap(); } }); - }, + } "<" => { gst.seek_backward()?; renderer.as_ref().map(|r| { @@ -278,19 +253,24 @@ fn read_keys(gst: Gstreamer, "u" => gst.unmute()?, "xy" => { if let Some(ref renderer) = renderer { - let xsize = stdin.read_line()? + let xsize = stdin + .read_line()? .unwrap_or(String::from("0")) .parse::()?; - let ysize = stdin.read_line()? + let ysize = stdin + .read_line()? .unwrap_or(String::from("0")) .parse::()?; - let mut xpix = stdin.read_line()? + let mut xpix = stdin + .read_line()? .unwrap_or(String::from("0")) .parse::()?; - let mut ypix = stdin.read_line()? + let mut ypix = stdin + .read_line()? .unwrap_or(String::from("0")) .parse::()?; - let cell_ratio = stdin.read_line()? + let cell_ratio = stdin + .read_line()? .unwrap_or(String::from("0")) .parse::()?; let mut renderer = renderer @@ -299,10 +279,9 @@ fn read_keys(gst: Gstreamer, if renderer.target == RenderTarget::Unicode { xpix = xsize; - ypix = ysize*2; + ypix = ysize * 2; } - renderer.set_widget_size(xsize, ysize, xpix, ypix, cell_ratio)?; match renderer.last_frame { Some(ref sample) => { @@ -311,8 +290,6 @@ fn read_keys(gst: Gstreamer, } _ => {} } - - } } _ => {} @@ -321,10 +298,7 @@ fn read_keys(gst: Gstreamer, } #[cfg(feature = "video")] -pub fn audio_preview(path: &String, - autoplay: bool, - mute: bool) - -> MResult<()> { +pub fn audio_preview(path: &String, autoplay: bool, mute: bool) -> MResult<()> { let gst = Gstreamer::new(path)?; let tgst = gst.clone(); @@ -342,7 +316,7 @@ pub fn audio_preview(path: &String, // Just redo loop until position changes if last_pos == Some(position) { - continue + continue; } last_pos = Some(position); @@ -355,7 +329,6 @@ pub fn audio_preview(path: &String, writeln!(stdout, "{}", duration)?; stdout.flush()?; } - }); if autoplay && !mute { @@ -378,24 +351,24 @@ struct Gstreamer { #[cfg(feature = "video")] impl Gstreamer { fn new(file: &str) -> MResult { - use gstreamer::{Element, ElementFactory, GhostPad, Bin}; + use gstreamer::{Bin, Element, ElementFactory, GhostPad}; gstreamer::init()?; - let player = ElementFactory::make("playbin", None) - .ok_or(format_err!("Can't create playbin"))?; + let player = + ElementFactory::make("playbin", None).ok_or(format_err!("Can't create playbin"))?; let videorate = ElementFactory::make("videorate", None) .ok_or(format_err!("Can't create videorate element"))?; - let sink = ElementFactory::make("appsink", None) - .ok_or(format_err!("Can't create appsink"))?; + let sink = + ElementFactory::make("appsink", None).ok_or(format_err!("Can't create appsink"))?; - let appsink = sink.clone() - .downcast::() - .unwrap(); + let appsink = sink.clone().downcast::().unwrap(); - let elems = &[&videorate, //&videoscale, - &sink]; + let elems = &[ + &videorate, //&videoscale, + &sink, + ]; let bin = Bin::new(None); @@ -404,8 +377,8 @@ impl Gstreamer { // make input for bin point to first element let sink = elems[0].get_static_pad("sink").unwrap(); - let ghost = GhostPad::new(Some("sink"), &sink) - .ok_or(format_err!("Can't create GhostPad"))?; + let ghost = + GhostPad::new(Some("sink"), &sink).ok_or(format_err!("Can't create GhostPad"))?; ghost.set_active(true)?; bin.add_pad(&ghost)?; @@ -437,9 +410,7 @@ impl Gstreamer { let state = self.get_state(); self.pause()?; - - let appsink = self.appsink.clone() - .upcast::(); + let appsink = self.appsink.clone().upcast::(); Element::unlink_many(&[&self.videorate, &appsink]); @@ -450,19 +421,19 @@ impl Gstreamer { std::thread::sleep(std::time::Duration::from_millis(100)); self.player.set_state(state)?; - - Ok(()) } - pub fn process_first_frame(&self, - renderer: &Arc>) -> MResult<()> { + pub fn process_first_frame(&self, renderer: &Arc>) -> MResult<()> { self.pause()?; - let sample = self.appsink.pull_preroll() + let sample = self + .appsink + .pull_preroll() .ok_or_else(|| format_err!("Couldn't read first frame!"))?; - let (max_x, max_y) = renderer.read() + let (max_x, max_y) = renderer + .read() .map_err(|_| format_err!("Failed at locking renderer!"))? .max_size_pix(&sample); @@ -471,10 +442,11 @@ impl Gstreamer { Ok(()) } - - pub fn send_preroll(&self, - renderer: &Arc>) -> MResult<()> { - let appsink = self.appsink.downcast_ref::().unwrap(); + pub fn send_preroll(&self, renderer: &Arc>) -> MResult<()> { + let appsink = self + .appsink + .downcast_ref::() + .unwrap(); let sample = appsink.pull_preroll().unwrap(); let pos = self.position(); let dur = self.duration(); @@ -484,19 +456,18 @@ impl Gstreamer { pub fn set_scaling(&self, x: usize, y: usize) -> MResult<()> { use gstreamer::Caps; - let caps = - format!("video/x-raw,format=RGBA,width={},height={}", - x, - y); + let caps = format!("video/x-raw,format=RGBA,width={},height={}", x, y); let caps = Caps::from_string(&caps).unwrap(); self.change_format(caps) } pub fn get_rate(&self) -> MResult { - let rate = self.videorate + let rate = self + .videorate .get_property("max-rate")? - .downcast::().unwrap() + .downcast::() + .unwrap() .get() .ok_or_else(|| format_err!("No video rate???"))?; @@ -514,13 +485,15 @@ impl Gstreamer { } pub fn position(&self) -> usize { - self.player.query_position::() + self.player + .query_position::() .map(|p| p.seconds().unwrap_or(0)) .unwrap_or(0) as usize } pub fn duration(&self) -> usize { - self.player.query_duration::() + self.player + .query_duration::() .map(|d| d.seconds().unwrap_or(0)) .unwrap_or(0) as usize } @@ -571,53 +544,48 @@ impl Gstreamer { pub fn seek_forward(&self) -> MResult<()> { let seek_time = gstreamer::ClockTime::from_seconds(5); - if let Some(mut time) = self.player - .query_position::() { - time += seek_time; - - self.player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::Time(time) - )?; - } + if let Some(mut time) = self.player.query_position::() { + time += seek_time; + + self.player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::Time(time), + )?; + } Ok(()) } pub fn seek_backward(&self) -> MResult<()> { let seek_time = gstreamer::ClockTime::from_seconds(5); - if let Some(mut time) = self.player - .query_position::() { - if time >= seek_time { - time -= seek_time; - } else { - time = gstreamer::ClockTime(Some(0)); - } - - self.player.seek_simple( - gstreamer::SeekFlags::FLUSH, - gstreamer::format::GenericFormattedValue::Time(time) - )?; + if let Some(mut time) = self.player.query_position::() { + if time >= seek_time { + time -= seek_time; + } else { + time = gstreamer::ClockTime(Some(0)); } + + self.player.seek_simple( + gstreamer::SeekFlags::FLUSH, + gstreamer::format::GenericFormattedValue::Time(time), + )?; + } Ok(()) } } - trait WithRaw { - fn with_raw(&self, - fun: impl FnOnce(&[u8]) -> MResult<()>) - -> MResult<()>; + fn with_raw(&self, fun: impl FnOnce(&[u8]) -> MResult<()>) -> MResult<()>; } #[cfg(feature = "video")] impl WithRaw for gstreamer::Sample { - fn with_raw(&self, - fun: impl FnOnce(&[u8]) -> MResult<()>) - -> MResult<()> { - let buffer = self.get_buffer() + fn with_raw(&self, fun: impl FnOnce(&[u8]) -> MResult<()>) -> MResult<()> { + let buffer = self + .get_buffer() .ok_or(format_err!("Couldn't get buffer from frame!"))?; - let map = buffer.map_readable() + let map = buffer + .map_readable() .ok_or(format_err!("Couldn't get buffer from frame!"))?; fun(map.as_slice()) @@ -626,9 +594,7 @@ impl WithRaw for gstreamer::Sample { // Mostly for plain old images, since they come from image::open impl WithRaw for RgbaImage { - fn with_raw(&self, - fun: impl FnOnce(&[u8]) -> MResult<()>) - -> MResult<()> { + fn with_raw(&self, fun: impl FnOnce(&[u8]) -> MResult<()>) -> MResult<()> { let bytes = self.as_flat_samples(); fun(bytes.as_slice()) @@ -640,38 +606,42 @@ enum RenderTarget { Unicode, #[cfg(feature = "sixel")] Sixel, - Kitty + Kitty, } impl RenderTarget { - fn send_image(&self, - img: &(impl WithRaw+ImgSize), - context: &Renderer) -> MResult<()> { + fn send_image(&self, img: &(impl WithRaw + ImgSize), context: &Renderer) -> MResult<()> { match self { #[cfg(feature = "sixel")] RenderTarget::Sixel => self.print_sixel(img)?, RenderTarget::Unicode => self.print_unicode(img)?, - RenderTarget::Kitty => self.print_kitty(img, context)? + RenderTarget::Kitty => self.print_kitty(img, context)?, } Ok(()) } - fn print_unicode(&self, img: &(impl WithRaw+ImgSize)) -> MResult<()> { + fn print_unicode(&self, img: &(impl WithRaw + ImgSize)) -> MResult<()> { let (xsize, _) = img.size()?; img.with_raw(move |raw| -> MResult<()> { - let lines = raw.chunks(4*xsize*2).map(|two_lines_colors| { - let (upper_line,lower_line) = two_lines_colors.split_at(4*xsize); - upper_line.chunks(4) - .zip(lower_line.chunks(4)) - .map(|(upper, lower)| { - format!("{}{}▀{}", + let lines = raw + .chunks(4 * xsize * 2) + .map(|two_lines_colors| { + let (upper_line, lower_line) = two_lines_colors.split_at(4 * xsize); + upper_line + .chunks(4) + .zip(lower_line.chunks(4)) + .map(|(upper, lower)| { + format!( + "{}{}▀{}", Fg(Rgb(upper[0], upper[1], upper[2])), Bg(Rgb(lower[0], lower[1], lower[2])), termion::style::Reset - ) - }).collect::() - }).collect::>(); + ) + }) + .collect::() + }) + .collect::>(); for line in lines { println!("{}", line); @@ -683,10 +653,8 @@ impl RenderTarget { }) } - fn print_kitty(&self, - img: &(impl WithRaw+ImgSize), - context: &Renderer) -> MResult<()> { - let (w,h) = context.max_size(img); + fn print_kitty(&self, img: &(impl WithRaw + ImgSize), context: &Renderer) -> MResult<()> { + let (w, h) = context.max_size(img); let (img_x, img_y) = img.size()?; img.with_raw(move |raw| -> MResult<()> { @@ -698,12 +666,10 @@ impl RenderTarget { let path = base64::encode("/tmp/img.raw"); print!("\x1b_Ga=d\x1b\\"); - println!("\x1b_Gf=32,s={},v={},c={},r={},a=T,t=f;{}\x1b\\", - img_x, - img_y, - w, - h, - path); + println!( + "\x1b_Gf=32,s={},v={},c={},r={},a=T,t=f;{}\x1b\\", + img_x, img_y, w, h, path + ); println!(""); Ok(()) @@ -711,7 +677,7 @@ impl RenderTarget { } #[cfg(feature = "sixel")] - fn print_sixel(&self, img: &(impl WithRaw+ImgSize)) -> MResult<()> { + fn print_sixel(&self, img: &(impl WithRaw + ImgSize)) -> MResult<()> { use sixel_rs::encoder::{Encoder, QuickFrameBuilder}; use sixel_rs::optflags::EncodePolicy; @@ -719,10 +685,10 @@ impl RenderTarget { img.with_raw(move |raw| -> MResult<()> { let sixfail = |e| format_err!("Sixel failed with: {:?}", e); - let encoder = Encoder::new() - .map_err(sixfail)?; + let encoder = Encoder::new().map_err(sixfail)?; - encoder.set_encode_policy(EncodePolicy::Fast) + encoder + .set_encode_policy(EncodePolicy::Fast) .map_err(sixfail)?; let frame = QuickFrameBuilder::new() @@ -731,8 +697,7 @@ impl RenderTarget { .format(sixel_sys::PixelFormat::RGBA8888) .pixels(raw.to_vec()); - encoder.encode_bytes(frame) - .map_err(sixfail)?; + encoder.encode_bytes(frame).map_err(sixfail)?; // No end of line printed by encoder println!(""); @@ -759,22 +724,26 @@ struct Renderer { } impl Renderer { - fn new(target: RenderTarget, - xsize: usize, - ysize: usize, - mut xpix: usize, - mut ypix: usize, - cell_ratio: f32) -> Renderer { - + fn new( + target: RenderTarget, + xsize: usize, + ysize: usize, + mut xpix: usize, + mut ypix: usize, + cell_ratio: f32, + ) -> Renderer { #[cfg(feature = "sixel")] match std::env::var("TERM") { Ok(term) => { - if term == "xterm" && - target == RenderTarget::Sixel { - // xterm has a hard limit on graphics size - // maybe splitting the image into parts would work? - if xpix > 1000 { xpix = 1000 }; - if ypix > 1000 { ypix = 1000 }; + if term == "xterm" && target == RenderTarget::Sixel { + // xterm has a hard limit on graphics size + // maybe splitting the image into parts would work? + if xpix > 1000 { + xpix = 1000 + }; + if ypix > 1000 { + ypix = 1000 + }; } } _ => {} @@ -797,12 +766,14 @@ impl Renderer { } #[cfg(feature = "video")] - fn set_widget_size(&mut self, - xsize: usize, - ysize: usize, - xpix: usize, - ypix: usize, - cell_ratio: f32) -> MResult<()> { + fn set_widget_size( + &mut self, + xsize: usize, + ysize: usize, + xpix: usize, + ypix: usize, + cell_ratio: f32, + ) -> MResult<()> { self.xsize = xsize; self.ysize = ysize; self.xpix = xpix; @@ -817,28 +788,26 @@ impl Renderer { fn send_media_meta(&self, frame: &impl ImgSize) -> MResult<()> { let (_, height) = self.max_size(frame); - println!("{}", height+1); + println!("{}", height + 1); println!("{}", self.position); println!("{}", self.duration); Ok(()) } - - - - fn send_image(&self, image: &(impl WithRaw+ImgSize)) -> MResult<()> { + fn send_image(&self, image: &(impl WithRaw + ImgSize)) -> MResult<()> { self.target.send_image(image, &self)?; Ok(()) } #[cfg(feature = "video")] - fn new_frame(&mut self, - frame: gstreamer::sample::Sample, - position: usize, - duration: usize) - -> MResult<()> { + fn new_frame( + &mut self, + frame: gstreamer::sample::Sample, + position: usize, + duration: usize, + ) -> MResult<()> { self.position = position; self.duration = duration; @@ -856,16 +825,18 @@ impl Renderer { self.last_frame.as_ref().map(|frame| { let (xpix, ypix) = frame.size()?; frame.with_raw(|raw| { - let img = ImageBuffer::, Vec>::from_raw(xpix as u32, - ypix as u32, - raw.to_vec()) - .ok_or(format_err!("Couldn't load last frame for rescaling!"))?; + let img = ImageBuffer::, Vec>::from_raw( + xpix as u32, + ypix as u32, + raw.to_vec(), + ) + .ok_or(format_err!("Couldn't load last frame for rescaling!"))?; let img = DynamicImage::ImageRgba8(img); let (max_x, max_y) = self.max_size_pix(&img); - let img = img.resize_exact(max_x as u32, - max_y as u32, - image::FilterType::Gaussian).to_rgba(); + let img = img + .resize_exact(max_x as u32, max_y as u32, image::FilterType::Gaussian) + .to_rgba(); self.send_image(&img)?; self.send_media_meta(&img)?; @@ -875,23 +846,19 @@ impl Renderer { Ok(()) } - pub fn max_size(&self, image: &impl ImgSize) -> (usize, usize) - { + pub fn max_size(&self, image: &impl ImgSize) -> (usize, usize) { let xsize = self.xsize; let ysize = self.ysize; let (img_xsize, img_ysize) = image.size().unwrap(); // Cells are not square, but almost 2:1 let img_ratio = (img_xsize as f32 / img_ysize as f32) / self.cell_ratio; - let (new_x, new_y) = fill_ratio(img_ratio, xsize, ysize); - (new_x as usize, new_y as usize) } - pub fn max_size_pix(&self, image: &impl ImgSize) -> (usize, usize) - { + pub fn max_size_pix(&self, image: &impl ImgSize) -> (usize, usize) { let xsize = self.xpix; let ysize = self.ypix; let (img_xsize, img_ysize) = image.size().unwrap(); @@ -916,7 +883,7 @@ fn fill_ratio(ratio: f32, max_x: usize, max_y: usize) -> (usize, usize) { if ratio < 1 as f32 { new_x = (max_y as f32 * ratio) as usize; new_y = max_y; - // short / wide + // short / wide } else { new_x = max_x; new_y = (max_x as f32 / ratio) as usize; diff --git a/src/icon.rs b/src/icon.rs index 9916804..8ce8edf 100644 --- a/src/icon.rs +++ b/src/icon.rs @@ -1,8 +1,8 @@ // Stolen from lsd: https://github.com/Peltoche/lsd // Apache License 2.0 -use std::path::PathBuf; use std::collections::HashMap; +use std::path::PathBuf; pub struct Icons { icons_by_name: HashMap<&'static str, &'static str>, @@ -17,13 +17,12 @@ pub struct Icons { // s#\\u[0-9a-f]*#\=eval('"'.submatch(0).'"')# impl Icons { pub fn new() -> Self { - let (icons_by_name, - icons_by_extension, - default_file_icon, - default_folder_icon) = (Self::get_default_icons_by_name(), - Self::get_default_icons_by_extension(), - "\u{f016}", //  - "\u{f115}"); //  + let (icons_by_name, icons_by_extension, default_file_icon, default_folder_icon) = ( + Self::get_default_icons_by_name(), + Self::get_default_icons_by_extension(), + "\u{f016}", //  + "\u{f115}", + ); //  Self { icons_by_name, @@ -34,12 +33,12 @@ impl Icons { } pub fn get(&self, name: &PathBuf) -> &'static str { - let file_name = name.file_name() + let file_name = name + .file_name() .and_then(|name| name.to_str()) .unwrap_or(""); - let extension = name.extension() - .and_then(|ext| ext.to_str()); + let extension = name.extension().and_then(|ext| ext.to_str()); // Check the known names. if let Some(icon) = self.icons_by_name.get(file_name) { diff --git a/src/imgview.rs b/src/imgview.rs index 6143add..027af87 100644 --- a/src/imgview.rs +++ b/src/imgview.rs @@ -1,14 +1,12 @@ +use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::io::{BufReader, BufRead}; use std::sync::atomic::{AtomicU32, Ordering}; -use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; -use crate::fail::{HResult, ErrorCause}; +use crate::fail::{ErrorCause, HError, HResult}; use crate::mediaview::MediaError; - - +use crate::widget::{Widget, WidgetCore}; lazy_static! { static ref PID: AtomicU32 = AtomicU32::new(0); @@ -39,13 +37,13 @@ impl ImgView { let (xpix, ypix) = self.core.coordinates.size_pixels()?; let cell_ratio = crate::term::cell_ratio()?; - let file = &self.file.as_ref()?; + let file = &self.file.as_ref().ok_or_else(|| HError::NoneError)?; let media_previewer = self.core.config().media_previewer; let g_mode = self.core.config().graphics; let mut previewer = Command::new(&media_previewer) - .arg(format!("{}", (xsize+1))) - .arg(format!("{}", (ysize+1))) + .arg(format!("{}", (xsize + 1))) + .arg(format!("{}", (ysize + 1))) .arg(format!("{}", xpix)) .arg(format!("{}", ypix)) .arg(format!("{}", cell_ratio)) @@ -59,11 +57,13 @@ impl ImgView { .stderr(Stdio::piped()) .spawn() .map_err(|e| { - let msg = format!("Couldn't run {}{}{}! Error: {:?}", - crate::term::color_red(), - media_previewer, - crate::term::normal_color(), - &e.kind()); + let msg = format!( + "Couldn't run {}{}{}! Error: {:?}", + crate::term::color_red(), + media_previewer, + crate::term::normal_color(), + &e.kind() + ); self.core.show_status(&msg).ok(); @@ -72,17 +72,13 @@ impl ImgView { PID.store(previewer.id(), Ordering::Relaxed); - let stdout = previewer.stdout - .take() - .unwrap(); + let stdout = previewer.stdout.take().unwrap(); let output = BufReader::new(stdout) .lines() .collect::, _>>()?; - let stderr = previewer.stderr - .take() - .unwrap(); + let stderr = previewer.stderr.take().unwrap(); let stderr = BufReader::new(stderr) .lines() @@ -94,9 +90,8 @@ impl ImgView { if !status.success() { match status.code() { - Some(code) => Err(MediaError::MediaViewerFailed(code, - ErrorCause::Str(stderr)))?, - None => Err(MediaError::MediaViewerKilled)? + Some(code) => Err(MediaError::MediaViewerFailed(code, ErrorCause::Str(stderr)))?, + None => Err(MediaError::MediaViewerKilled)?, } } @@ -114,12 +109,16 @@ impl ImgView { } pub fn kill_running() { - use nix::{unistd::Pid, - sys::signal::{kill, Signal}}; + use nix::{ + sys::signal::{kill, Signal}, + unistd::Pid, + }; let pid = PID.load(Ordering::Relaxed); - if pid == 0 { return; } + if pid == 0 { + return; + } let pid = Pid::from_raw(pid as i32); kill(pid, Signal::SIGTERM).ok(); @@ -128,7 +127,6 @@ impl ImgView { } } - impl Widget for ImgView { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) @@ -139,7 +137,9 @@ impl Widget for ImgView { } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { - if &self.core.coordinates == coordinates { return Ok(()) } + if &self.core.coordinates == coordinates { + return Ok(()); + } self.core.coordinates = coordinates.clone(); if self.file.is_some() { @@ -150,22 +150,21 @@ impl Widget for ImgView { } fn refresh(&mut self) -> HResult<()> { - Ok(()) } fn get_drawlist(&self) -> HResult { let (xpos, ypos) = self.core.coordinates.position_u(); - let mut draw = self.buffer - .iter() - .enumerate() - .fold(String::new(), |mut draw, (pos, line)| { - draw += &format!("{}", crate::term::goto_xy_u(xpos, - ypos + pos)); - draw += line; - draw - }); + let mut draw = + self.buffer + .iter() + .enumerate() + .fold(String::new(), |mut draw, (pos, line)| { + draw += &format!("{}", crate::term::goto_xy_u(xpos, ypos + pos)); + draw += line; + draw + }); draw += &format!("{}", termion::style::Reset); diff --git a/src/keybind.rs b/src/keybind.rs index 64a725a..d5a599b 100644 --- a/src/keybind.rs +++ b/src/keybind.rs @@ -1,31 +1,26 @@ -use termion::event::Key; use ini::Ini; use strum::IntoEnumIterator; +use termion::event::Key; use std::collections::HashMap; use std::default::Default; +use std::fmt::{Debug, Display}; use std::str::FromStr; -use std::fmt::{Display, Debug}; -use crate::fail::{HError, HResult, KeyBindError, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult, KeyBindError}; use crate::widget::Widget; - pub type KbResult = Result; - #[derive(Clone, Debug)] pub struct Bindings(HashMap); impl Bindings { - pub fn get(&self, - key: impl Into) -> Option<&T> { + pub fn get(&self, key: impl Into) -> Option<&T> { self.0.get(&key.into()) } - pub fn insert(&mut self, - key: impl Into, - value: T) -> Option { + pub fn insert(&mut self, key: impl Into, value: T) -> Option { self.0.insert(key.into(), value) } @@ -34,9 +29,6 @@ impl Bindings { } } - - - pub trait Acting where Self: Widget, @@ -56,24 +48,20 @@ where let gkey = AnyKey::from(key); // Moving takes priority - if let Some(movement) = self.get_core()? - .config() - .keybinds - .movement - .get(gkey) { - match self.movement(movement) { - Ok(()) => return Ok(()), - Err(HError::KeyBind(KeyBindError::MovementUndefined)) => {} - Err(e) => Err(e)? - } + if let Some(movement) = self.get_core()?.config().keybinds.movement.get(gkey) { + match self.movement(movement) { + Ok(()) => return Ok(()), + Err(HError::KeyBind(KeyBindError::MovementUndefined)) => {} + Err(e) => Err(e)?, } + } self.search_in(); let bindings = self.search_in(); if let Some(action) = bindings.get(key) { - return self.do_action(action) + return self.do_action(action); } else if let Some(any_key) = gkey.any() { if let Some(action) = bindings.get(any_key) { let action = action.insert_key_param(key); @@ -85,7 +73,6 @@ where } } - #[derive(Clone, Debug)] pub struct KeyBinds { pub movement: Bindings, @@ -114,17 +101,15 @@ impl Default for KeyBinds { minibuffer: Bindings::default(), fold: Bindings::default(), log: Bindings::default(), - quickaction: Bindings::default() + quickaction: Bindings::default(), } } } - impl KeyBinds { pub fn load() -> HResult { let bindings_path = crate::paths::bindings_path()?; - let ini = Ini::load_from_file_noescape(bindings_path) - .map_err(KeyBindError::from)?; + let ini = Ini::load_from_file_noescape(bindings_path).map_err(KeyBindError::from)?; let movement = Movement::load_section(&ini); let filebrowser = FileBrowserAction::load_section(&ini); @@ -149,7 +134,7 @@ impl KeyBinds { minibuffer, fold, log, - quickaction + quickaction, }) } } @@ -160,13 +145,13 @@ pub enum AnyKey { AnyChar, AnyF, AnyCtrl, - AnyAlt + AnyAlt, } impl Display for AnyKey { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use AnyKey::*; use termion::event::Key::*; + use AnyKey::*; match self { Key(key) => match key { @@ -174,27 +159,27 @@ impl Display for AnyKey { Alt(ch) => write!(fmt, "M-{}", ch), Ctrl(ch) => write!(fmt, "C-{}", ch), F(n) => write!(fmt, "F{}", n), - k @ _ => write!(fmt, "{:?}", k) - } + k @ _ => write!(fmt, "{:?}", k), + }, AnyChar => write!(fmt, "_"), AnyF => write!(fmt, "F_"), AnyCtrl => write!(fmt, "C-_"), - AnyAlt => write!(fmt, "M-_") + AnyAlt => write!(fmt, "M-_"), } } } impl AnyKey { pub fn any(&self) -> Option { - use AnyKey::*; use termion::event::Key::*; + use AnyKey::*; match self { Key(F(_)) => Some(AnyF), Key(Char(_)) => Some(AnyChar), Key(Ctrl(_)) => Some(AnyCtrl), Key(Alt(_)) => Some(AnyAlt), - _ => None + _ => None, } } } @@ -209,42 +194,37 @@ impl FromStr for AnyKey { type Err = KeyBindError; fn from_str(key: &str) -> Result { - use AnyKey::*; use termion::event::Key::*; + use AnyKey::*; - let key_err = |key: &str| { - KeyBindError::ParseKeyError(key.to_string()) - }; - - let predefined = |key| { - match key { - "Backspace" => Some(Key(Backspace)), - "Left" => Some(Key(Left)), - "Right" => Some(Key(Right)), - "Up" => Some(Key(Up)), - "Down" => Some(Key(Down)), - "Home" => Some(Key(Home)), - "End" => Some(Key(End)), - "PageUp" => Some(Key(PageUp)), - "PageDown" => Some(Key(PageDown)), - "Delete" => Some(Key(Delete)), - "Insert" => Some(Key(Insert)), - "Tab" => Some(Key(Char('\t'))), - "BackTab" => Some(Key(BackTab)), - "Enter" => Some(Key(Char('\n'))), - "Space" => Some(Key(Char(' '))), - "\\_" => Some(Key(Char('_'))), - "_" => Some(AnyChar), - "Esc" => Some(Key(Esc)), - _ => None - } + let key_err = |key: &str| KeyBindError::ParseKeyError(key.to_string()); + + let predefined = |key| match key { + "Backspace" => Some(Key(Backspace)), + "Left" => Some(Key(Left)), + "Right" => Some(Key(Right)), + "Up" => Some(Key(Up)), + "Down" => Some(Key(Down)), + "Home" => Some(Key(Home)), + "End" => Some(Key(End)), + "PageUp" => Some(Key(PageUp)), + "PageDown" => Some(Key(PageDown)), + "Delete" => Some(Key(Delete)), + "Insert" => Some(Key(Insert)), + "Tab" => Some(Key(Char('\t'))), + "BackTab" => Some(Key(BackTab)), + "Enter" => Some(Key(Char('\n'))), + "Space" => Some(Key(Char(' '))), + "\\_" => Some(Key(Char('_'))), + "_" => Some(AnyChar), + "Esc" => Some(Key(Esc)), + _ => None, }; if let Some(key) = predefined(key) { return Ok(key); } - if key.starts_with("F") && key.len() == 2 { let chr = key.get(1..2); @@ -252,13 +232,8 @@ impl FromStr for AnyKey { Ok(AnyF) } else { chr.ok_or_else(|| key_err(key)) - .and_then(|num| - num - .parse() - .map(|n| - Key(F(n))) - .map_err(|_| key_err(key))) - } + .and_then(|num| num.parse().map(|n| Key(F(n))).map_err(|_| key_err(key))) + } } else if let Ok(key) = key.parse() { Ok(Key(Char(key))) } else { @@ -268,41 +243,40 @@ impl FromStr for AnyKey { // Something is wrong if there are more parts return Err(key_err(key)); } else { - (parts.get(0).and_then(|p| p.parse().ok()), - parts.get(1).and_then(|p| p.parse().ok())) + ( + parts.get(0).and_then(|p| p.parse().ok()), + parts.get(1).and_then(|p| p.parse().ok()), + ) }; match (modifier, maybe_key) { (Some(ch), None) => match ch { '_' => Ok(AnyChar), - _ => Ok(Key(Char(ch))) - } + _ => Ok(Key(Char(ch))), + }, (Some('C'), Some(ch)) => match ch { '_' => Ok(AnyCtrl), - _ => Ok(Key(Ctrl(ch))) - } + _ => Ok(Key(Ctrl(ch))), + }, (Some('A'), Some(ch)) => match ch { '_' => Ok(AnyAlt), - _ => Ok(Key(Alt(ch))) - } + _ => Ok(Key(Alt(ch))), + }, (Some('M'), Some(ch)) => match ch { '_' => Ok(AnyAlt), - _ => Ok(Key(Alt(ch))) - } - _ => Err(key_err(key)) + _ => Ok(Key(Alt(ch))), + }, + _ => Err(key_err(key)), } } } } - - - #[derive(Copy, Clone, Display, Debug)] pub enum CharOrNum { Char(char), Num(usize), - Any + Any, } impl CharOrNum { @@ -311,8 +285,8 @@ impl CharOrNum { CharOrNum::Char(ch) => ch, CharOrNum::Any => default, _ => { - KeyBindError::CharOrNumWrongType(String::from("char"), - String::from("number")).log(); + KeyBindError::CharOrNumWrongType(String::from("char"), String::from("number")) + .log(); default } } @@ -323,8 +297,8 @@ impl CharOrNum { CharOrNum::Num(num) => num, CharOrNum::Any => default, _ => { - KeyBindError::CharOrNumWrongType(String::from("number"), - String::from("char")).log(); + KeyBindError::CharOrNumWrongType(String::from("number"), String::from("char")) + .log(); default } } @@ -335,11 +309,10 @@ impl FromStr for CharOrNum { type Err = KeyBindError; fn from_str(s: &str) -> Result { - let s = s.trim_start_matches("(") - .trim_end_matches(")"); + let s = s.trim_start_matches("(").trim_end_matches(")"); if s == "_" { - return Ok(Self::Any) + return Ok(Self::Any); } if let Ok(num) = s.parse() { @@ -352,35 +325,32 @@ impl FromStr for CharOrNum { } } - - - pub trait BindingSection where Self: FromStr + Copy + Display + Debug, - Bindings: Default + Bindings: Default, { fn section() -> &'static str; fn process_action_str(action_str: &str) -> (&str, Option) { // Could be something like Up(10) for going up 10 times - action_str.rfind("(") + action_str + .rfind("(") .map(|split_pos| { let split = action_str.split_at(split_pos); let action = split.0; - let param = split.1 - .trim_start_matches("(") - .trim_end_matches(")"); + let param = split.1.trim_start_matches("(").trim_end_matches(")"); (action, param.parse().log_and().ok()) - }).unwrap_or((action_str, None)) - + }) + .unwrap_or((action_str, None)) } // statically inserts hardcoded stuff from config like "Up(10)" into action fn insert_config_param(self, param: CharOrNum) -> Self { - let msg = format!("Warning: Unsupported config parameter {:?} for {}", - param, - self); + let msg = format!( + "Warning: Unsupported config parameter {:?} for {}", + param, self + ); HError::log::<()>(&msg).ok(); self } @@ -396,7 +366,9 @@ where } fn parse_section(ini: &Ini) -> HResult> { - let section = ini.section(Some(Self::section()))?; + let section = ini + .section(Some(Self::section())) + .ok_or_else(|| HError::NoneError)?; let mut bindings = Bindings::new(); @@ -406,26 +378,33 @@ where let action = Self::from_str(action_str) .map_err(|_| KeyBindError::WrongAction(action_str.to_string())) .map_err(HError::from) - .map(|act| - if let Some(cp) = config_param { - act.insert_config_param(cp) - } else { - // default() values on e.g. usize are often useless for actions - act.as_default() - }); + .map(|act| { + if let Some(cp) = config_param { + act.insert_config_param(cp) + } else { + // default() values on e.g. usize are often useless for actions + act.as_default() + } + }); // If action isn't valid log it and try next binding - if action.is_err() { action.log(); continue; } + if action.is_err() { + action.log(); + continue; + } for key_str in keys_str.split(",") { let key_str = key_str.trim(); - let key = key_str.parse::() - .map_err(|_| KeyBindError::WrongKey(action_str.to_string(), - key_str.to_string())); + let key = key_str.parse::().map_err(|_| { + KeyBindError::WrongKey(action_str.to_string(), key_str.to_string()) + }); // If key isn't valid log it and try next binding - if key.is_err() { key.log(); continue; } + if key.is_err() { + key.log(); + continue; + } bindings.insert(key?, action.clone()?); } @@ -441,13 +420,6 @@ where } } - - - - - - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum Movement { Up(usize), @@ -460,8 +432,6 @@ pub enum Movement { PageDown, } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum FileBrowserAction { LeftColumnDown, @@ -482,11 +452,9 @@ pub enum FileBrowserAction { RunSubshell, ToggleColumns, ZoomPreview, - ExecCmd + ExecCmd, } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum FileListAction { Search, @@ -506,8 +474,6 @@ pub enum FileListAction { ToggleDirsFirst, } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum TabAction { NewTab, @@ -517,8 +483,6 @@ pub enum TabAction { GotoTab(usize), } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum MediaAction { TogglePause, @@ -527,17 +491,13 @@ pub enum MediaAction { SeekBackward, } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum BookmarkAction { GotoLastCwd, Goto(char), - Delete(char) + Delete(char), } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum ProcessAction { Close, @@ -549,11 +509,9 @@ pub enum ProcessAction { ScrollOutputPageDown, ScrollOutputPageUp, ScrollOutputBottom, - ScrollOutputTop + ScrollOutputTop, } - - #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum MiniBufferAction { InsertChar(char), @@ -570,30 +528,25 @@ pub enum MiniBufferAction { ClearLine, DeleteWord, CursorToStart, - CursorToEnd + CursorToEnd, } #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum FoldAction { - ToggleFold + ToggleFold, } #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum LogAction { - Close + Close, } #[derive(EnumString, EnumIter, Copy, Clone, Display, Debug)] pub enum QuickActionAction { Close, - SelectOrRun(char) + SelectOrRun(char), } - - - - - impl BindingSection for Movement { fn section() -> &'static str { "movement" @@ -607,7 +560,7 @@ impl BindingSection for Movement { match self { Up(_) => Up(n), Down(_) => Down(n), - _ => self + _ => self, } } @@ -617,14 +570,11 @@ impl BindingSection for Movement { match self { Up(_) => Up(1), Down(_) => Down(1), - _ => self + _ => self, } } - - } - impl Default for Bindings { fn default() -> Self { use Movement::*; @@ -655,16 +605,14 @@ impl Default for Bindings { movement.insert(Key::Ctrl('v'), Movement::PageDown); movement.insert(Key::Ctrl('V'), Movement::PageUp); - movement } } - impl Default for Bindings { fn default() -> Self { - use Key::*; use FileBrowserAction::*; + use Key::*; let mut filebrowser = Bindings::new(); @@ -688,7 +636,7 @@ impl Default for Bindings { RunSubshell => Char('z'), ToggleColumns => Char('c'), ZoomPreview => Char('C'), - ExecCmd => Char('!') + ExecCmd => Char('!'), }; filebrowser.insert(key, action.as_default()); @@ -706,8 +654,8 @@ impl BindingSection for FileBrowserAction { impl Default for Bindings { fn default() -> Self { - use Key::*; use FileListAction::*; + use Key::*; let mut filelist = Bindings::new(); @@ -727,7 +675,7 @@ impl Default for Bindings { CycleSort => Char('s'), ToNextMtime => Char('K'), ToPrevMtime => Char('k'), - ToggleDirsFirst => Char('d') + ToggleDirsFirst => Char('d'), }; filelist.insert(key, action.as_default()); @@ -756,7 +704,7 @@ impl Default for Bindings { NextTab => Char('\t').into(), PrevTab => BackTab.into(), CloseTab => Ctrl('w').into(), - GotoTab(_) => AnyKey::AnyF + GotoTab(_) => AnyKey::AnyF, }; tab.insert(key, action.as_default()); @@ -778,14 +726,14 @@ impl BindingSection for TabAction { match self { GotoTab(_) => GotoTab(n), - _ => self + _ => self, } } fn insert_key_param(self, key: Key) -> Self { match (self, key) { (TabAction::GotoTab(_), Key::F(n)) => TabAction::GotoTab(n as usize - 1), - _ => self + _ => self, } } } @@ -802,7 +750,7 @@ impl Default for Bindings { TogglePause => Alt('m'), ToggleMute => Alt('M'), SeekForward => Alt('>'), - SeekBackward => Alt('<') + SeekBackward => Alt('<'), }; media.insert(key, action.as_default()); @@ -820,8 +768,8 @@ impl BindingSection for MediaAction { impl Default for Bindings { fn default() -> Self { - use Key::*; use BookmarkAction::*; + use Key::*; let mut bookmark = Bindings::new(); @@ -829,14 +777,12 @@ impl Default for Bindings { let key = match action { GotoLastCwd => Char('`').into(), Goto(_) => AnyKey::AnyChar, - BookmarkAction::Delete(_) => AnyKey::AnyAlt + BookmarkAction::Delete(_) => AnyKey::AnyAlt, }; bookmark.insert(key, action.as_default()); } - - bookmark } } @@ -846,7 +792,6 @@ impl BindingSection for BookmarkAction { "bookmarks" } - fn insert_config_param(self, param: CharOrNum) -> Self { use BookmarkAction::*; @@ -854,7 +799,7 @@ impl BindingSection for BookmarkAction { match self { Goto(_) => Goto(ch), - _ => self + _ => self, } } @@ -864,12 +809,11 @@ impl BindingSection for BookmarkAction { match (self, key) { (Goto(_), Key::Char(ch)) => Goto(ch), (Delete(_), Key::Char(ch)) => Delete(ch), - _ => self + _ => self, } } } - impl Default for Bindings { fn default() -> Self { use Key::*; @@ -888,7 +832,7 @@ impl Default for Bindings { ScrollOutputPageDown => Ctrl('v'), ScrollOutputPageUp => Ctrl('V'), ScrollOutputBottom => Char('>'), - ScrollOutputTop => Ctrl('<') + ScrollOutputTop => Ctrl('<'), }; process.insert(key, action.as_default()); @@ -904,7 +848,6 @@ impl BindingSection for ProcessAction { } } - impl Default for Bindings { fn default() -> Self { use termion::event::Key::*; @@ -929,8 +872,8 @@ impl Default for Bindings { ClearLine => Ctrl('u').into(), DeleteWord => Ctrl('h').into(), CursorToStart => Ctrl('a').into(), - CursorToEnd => Ctrl('e').into() - }; + CursorToEnd => Ctrl('e').into(), + }; minibuffer.insert(key, action.as_default()); } @@ -960,36 +903,33 @@ impl BindingSection for MiniBufferAction { match self { InsertChar(_) => InsertChar(ch), - _ => self + _ => self, } } fn insert_key_param(self, key: Key) -> Self { - use MiniBufferAction::*; use Key::*; + use MiniBufferAction::*; match (self, key) { (InsertChar(_), Char(ch)) => InsertChar(ch), (InsertTab(_), F(n)) => InsertTab(n as usize), - _ => self + _ => self, } } } - - - impl Default for Bindings { fn default() -> Self { - use Key::*; use FoldAction::*; + use Key::*; let mut fold = Bindings::new(); for action in FoldAction::iter() { let key = match action { - ToggleFold => Char('t') - }; + ToggleFold => Char('t'), + }; fold.insert(key, action.as_default()); } @@ -1013,7 +953,7 @@ impl Default for Bindings { for action in LogAction::iter() { let key = match action { - Close => Char('l') + Close => Char('l'), }; log.insert(key, action.as_default()); @@ -1031,8 +971,8 @@ impl BindingSection for LogAction { impl Default for Bindings { fn default() -> Self { - use AnyKey::*; use termion::event::Key::*; + use AnyKey::*; use QuickActionAction::*; let mut quickaction = Bindings::new(); @@ -1040,7 +980,7 @@ impl Default for Bindings { for action in QuickActionAction::iter() { let key = match action { Close => Key(Char('a')), - SelectOrRun(_) => AnyChar + SelectOrRun(_) => AnyChar, }; quickaction.insert(key, action.as_default()); @@ -1066,24 +1006,23 @@ impl BindingSection for QuickActionAction { match self { SelectOrRun(_) => SelectOrRun(ch), - _ => self + _ => self, } } fn insert_key_param(self, key: Key) -> Self { - use QuickActionAction::*; use Key::*; + use QuickActionAction::*; match (self, key) { (SelectOrRun(_), Char(ch)) => SelectOrRun(ch), (SelectOrRun(_), Ctrl(ch)) => SelectOrRun(ch), (SelectOrRun(_), Alt(ch)) => SelectOrRun(ch), - _ => self + _ => self, } } } - #[test] fn test_keyparse() { let keys = ["C-a", "A-_", "Delete", "a", "F9", "C-_"]; diff --git a/src/listview.rs b/src/listview.rs index 2c94ee3..9b469be 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -1,36 +1,44 @@ use std::fmt::Debug; use std::path::PathBuf; +use rayon::prelude::*; use termion::event::Key; use unicode_width::UnicodeWidthStr; -use rayon::prelude::*; use async_value::Stale; +use crate::dirty::Dirtyable; +use crate::fail::{ErrorLog, HError, HResult}; use crate::files::{File, Files}; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::fscache::FsCache; use crate::term; use crate::widget::{Widget, WidgetCore}; -use crate::dirty::Dirtyable; -use crate::fscache::FsCache; - pub trait Listable { type Item: Debug + PartialEq + Default; fn len(&self) -> usize; fn render(&self) -> Vec; - fn render_header(&self) -> HResult { Ok("".to_string()) } - fn render_footer(&self) -> HResult { Ok("".to_string()) } - fn on_new(&mut self) -> HResult<()> { Ok(()) } - fn on_refresh(&mut self) -> HResult<()> { Ok(()) } - fn on_key(&mut self, _key: Key) -> HResult<()> { Ok(()) } + fn render_header(&self) -> HResult { + Ok("".to_string()) + } + fn render_footer(&self) -> HResult { + Ok("".to_string()) + } + fn on_new(&mut self) -> HResult<()> { + Ok(()) + } + fn on_refresh(&mut self) -> HResult<()> { + Ok(()) + } + fn on_key(&mut self, _key: Key) -> HResult<()> { + Ok(()) + } } use crate::keybind::{Acting, Bindings, FileListAction, Movement}; - impl Acting for ListView { - type Action=FileListAction; + type Action = FileListAction; fn search_in(&self) -> Bindings { self.core.config().keybinds.filelist @@ -42,8 +50,18 @@ impl Acting for ListView { let pos = self.get_selection(); match movement { - Up(n) => { for _ in 0..*n { self.move_up(); }; self.refresh()?; } - Down(n) => { for _ in 0..*n { self.move_down(); }; self.refresh()?; } + Up(n) => { + for _ in 0..*n { + self.move_up(); + } + self.refresh()?; + } + Down(n) => { + for _ in 0..*n { + self.move_down(); + } + self.refresh()?; + } PageUp => self.page_up(), PageDown => self.page_down(), Top => self.move_top(), @@ -90,7 +108,7 @@ impl Listable for ListView { self.content.len() } - fn render(&self)-> Vec { + fn render(&self) -> Vec { self.render() } @@ -111,7 +129,7 @@ impl Listable for ListView { let meta_upto = self.content.meta_upto.unwrap_or(0); let ysize = self.core.coordinates.ysize_u(); - if self.offset + ysize >= meta_upto { + if self.offset + ysize >= meta_upto { let sender = self.core.get_sender(); let njobs = self.offset + ysize; @@ -137,7 +155,7 @@ impl Listable for ListView { #[derive(Debug, PartialEq)] pub struct ListView where - ListView: Listable + ListView: Listable, { pub content: T, pub current_item: Option< as Listable>::Item>, @@ -151,7 +169,7 @@ where impl ListView where ListView: Widget, - ListView: Listable + ListView: Listable, { pub fn new(core: &WidgetCore, content: T) -> ListView { let mut view = ListView:: { @@ -161,7 +179,7 @@ where offset: 0, core: core.clone(), seeking: false, - searching: None + searching: None, }; view.on_new().log(); view @@ -235,16 +253,14 @@ where self.offset = offset; self.selection = position; } - } #[derive(PartialEq)] pub enum FileSource { Path(File), - Files(Files) + Files(Files), } - pub struct FileListBuilder { core: WidgetCore, source: FileSource, @@ -301,25 +317,19 @@ impl FileListBuilder { let source = self.source; let selected_file = self.selected_file.take(); - // Run ticker for those nice loading animations (...) - crate::files::start_ticking(core.get_sender()); - // Already sorted let nosort = match source { FileSource::Files(_) => true, - _ => false + _ => false, }; - let mut files = - match source { - FileSource::Files(f) => Ok(f), - FileSource::Path(f) => { - c.as_ref() - .map_or_else(| | unreachable!(), - |c| s.map_or_else(| | c.get_files_sync(&f), - |s| c.get_files_sync_stale(&f, s))) - } - }?; + let mut files = match source { + FileSource::Files(f) => Ok(f), + FileSource::Path(f) => c.as_ref().map_or_else( + || unreachable!(), + |c| s.map_or_else(|| c.get_files_sync(&f), |s| c.get_files_sync_stale(&f, s)), + ), + }?; // Check/set hidden flag and recalculate number of files if it's different if !files.show_hidden == cfg.show_hidden() { @@ -335,8 +345,10 @@ impl FileListBuilder { let mut view = ListView::new(&core, files); selected_file - .or_else(|| c.as_ref() - .and_then(|c| c.get_selection(&view.content.directory).ok())) + .or_else(|| { + c.as_ref() + .and_then(|c| c.get_selection(&view.content.directory).ok()) + }) .map(|f| view.select_file(&f)); self.stale.map(|s| view.content.stale = Some(s)); @@ -344,36 +356,32 @@ impl FileListBuilder { view.content.set_clean(); view.core.set_clean(); - crate::files::stop_ticking(); - Ok(view) } } -impl ListView -{ +impl ListView { pub fn builder(core: WidgetCore, source: FileSource) -> FileListBuilder { FileListBuilder::new(core, source) } pub fn update_selected_file(&mut self, oldpos: usize) { let newpos = self.get_selection(); - let skip = - match newpos > oldpos { - true => newpos - oldpos, - false => 0 - }; + let skip = match newpos > oldpos { + true => newpos - oldpos, + false => 0, + }; - let seek_back = - match newpos < oldpos { - true => oldpos - newpos, - false => 0 - }; + let seek_back = match newpos < oldpos { + true => oldpos - newpos, + false => 0, + }; - let file = self.content - .iter_files_from(self.selected_file(), seek_back) - .skip(skip) - .nth(0); + let file = self + .content + .iter_files_from(self.selected_file(), seek_back) + .skip(skip) + .nth(0); self.current_item = file.cloned(); } @@ -388,20 +396,18 @@ impl ListView pub fn selected_file_mut(&mut self) -> &mut File { let selected_file = self.selected_file().clone(); - let file = self.content + let file = self + .content .iter_files_mut_from(&selected_file, 0) .nth(0) .map(|f| f as *mut File); - // Work around annoying restriction until polonius borrow checker becomes default // Since only ever one mutable borrow is returned this is perfectly safe // See also: https://github.com/rust-lang/rust/issues/21906 match file { Some(file) => unsafe { return file.as_mut().unwrap() }, - None => { - &mut self.content.directory - } + None => &mut self.content.directory, } } @@ -435,14 +441,14 @@ impl ListView let dir = &self.content.directory.path; let file = file.path; - HError::wrong_directory::<()>(dir.clone(), - file.clone()).log(); - let file = self.content - .iter_files() - .nth(0) - .cloned() - .or_else(|| File::new_placeholder(dir).ok()) - .unwrap(); + HError::wrong_directory::<()>(dir.clone(), file.clone()).log(); + let file = self + .content + .iter_files() + .nth(0) + .cloned() + .or_else(|| File::new_placeholder(dir).ok()) + .unwrap(); self.current_item = Some(file); self.set_selection(0); } @@ -455,7 +461,9 @@ impl ListView self.content.sort(); self.select_file(&file); self.refresh().log(); - self.core.show_status(&format!("Sorting by: {}", self.content.sort)).log(); + self.core + .show_status(&format!("Sorting by: {}", self.content.sort)) + .log(); } fn reverse_sort(&mut self) { @@ -464,8 +472,9 @@ impl ListView self.content.sort(); self.select_file(&file); self.refresh().log(); - self.core.show_status(&format!("Reversed sorting by: {}", - self.content.sort)).log(); + self.core + .show_status(&format!("Reversed sorting by: {}", self.content.sort)) + .log(); } fn select_next_mtime(&mut self) { @@ -484,7 +493,7 @@ impl ListView self.offset = 0; } else { self.move_down(); - } + } let file = self.clone_selected_file(); self.content.dirs_first = dir_settings; @@ -527,8 +536,12 @@ impl ListView let file = self.clone_selected_file(); self.content.toggle_hidden(); self.select_file(&file); - self.core.show_status(&format!("Showing hidden files: {}", - self.content.show_hidden)).log(); + self.core + .show_status(&format!( + "Showing hidden files: {}", + self.content.show_hidden + )) + .log(); } fn toggle_dirs_first(&mut self) { @@ -537,8 +550,9 @@ impl ListView self.content.sort(); self.select_file(&file); self.refresh().log(); - self.core.show_status(&format!("Direcories first: {}", - self.content.dirs_first)).log(); + self.core + .show_status(&format!("Direcories first: {}", self.content.dirs_first)) + .log(); } fn multi_select_file(&mut self) { @@ -569,8 +583,8 @@ impl ListView } if self.content.filter_selected && self.content.len() == 0 { - self.content.toggle_filter_selected(); - self.core.show_status("Disabled selection filter!").log(); + self.content.toggle_filter_selected(); + self.core.show_status("Disabled selection filter!").log(); } self.content.set_dirty(); @@ -587,15 +601,7 @@ impl ListView fn toggle_tag(&mut self) -> HResult<()> { self.selected_file_mut().toggle_tag()?; - - let oldpos = self.get_selection(); self.move_down(); - let newpos = self.get_selection(); - - if newpos > oldpos { - self.update_selected_file(oldpos); - } - Ok(()) } @@ -620,9 +626,7 @@ impl ListView match ev { Done(_) => {} NewInput(input) => { - let file = self.content - .find_file_with_name(&input) - .cloned(); + let file = self.content.find_file_with_name(&input).cloned(); file.map(|f| self.select_file(&f)); @@ -643,7 +647,7 @@ impl ListView } } } - _ => { } + _ => {} } break; } @@ -654,20 +658,22 @@ impl ListView if self.searching.is_none() { self.core.show_status("No search pattern set!").log(); } - let prev_search = self.searching.clone()?; + let prev_search = self.searching.clone().ok_or_else(|| HError::NoneError)?; let selection = self.get_selection(); - let file = self.content + let file = self + .content .files .iter() - .skip(selection+1) + .skip(selection + 1) .find(|file| { if file.name.to_lowercase().contains(&prev_search) { true } else { false } - }).clone(); + }) + .clone(); if let Some(file) = file { let file = file.clone(); @@ -682,24 +688,25 @@ impl ListView if self.searching.is_none() { self.core.show_status("No search pattern set!").log(); } - let prev_search = self.searching.clone()?; - + let prev_search = self.searching.clone().ok_or_else(|| HError::NoneError)?; self.reverse_sort(); let selection = self.get_selection(); - let file = self.content + let file = self + .content .files .iter() - .skip(selection+1) + .skip(selection + 1) .find(|file| { if file.name.to_lowercase().contains(&prev_search) { true } else { false } - }).cloned(); + }) + .cloned(); self.reverse_sort(); self.core.clear_status().log(); @@ -742,33 +749,31 @@ impl ListView let filter = self.core.minibuffer_continuous("filter"); match filter { - Err(HError::MiniBufferEvent(event)) => { - match event { - Done(filter) => { - self.core.show_status(&format!("Filtering with: \"{}\"", - &filter)).log(); + Err(HError::MiniBufferEvent(event)) => match event { + Done(filter) => { + self.core + .show_status(&format!("Filtering with: \"{}\"", &filter)) + .log(); - self.set_filter(Some(filter)); - } - NewInput(input) => { - self.set_filter(Some(input.clone())); - continue; - } - Empty => { - self.set_filter(None); - } - Cancelled => { - self.set_filter(prev_filter.take()); - self.select_file(&selected_file); - } - _ => {} + self.set_filter(Some(filter)); } - } + NewInput(input) => { + self.set_filter(Some(input.clone())); + continue; + } + Empty => { + self.set_filter(None); + } + Cancelled => { + self.set_filter(prev_filter.take()); + self.select_file(&selected_file); + } + _ => {} + }, _ => {} } break; - } Ok(()) @@ -792,8 +797,8 @@ impl ListView #[allow(trivial_bounds)] fn render_line_fn(&self) -> impl Fn(&File) -> String { - use std::fmt::Write; use crate::files::FileError; + use std::fmt::Write; let xsize = self.get_coordinates().unwrap().xsize(); let config = self.core.config(); @@ -806,7 +811,7 @@ impl ListView let (icon, icon_space) = match (icons, icons_space) { (true, true) => (file.icon(), " "), (true, false) => (file.icon(), ""), - _ => ("", "") + _ => ("", ""), }; let name = &file.name; @@ -817,94 +822,99 @@ impl ListView Err(HError::FileError(FileError::MetaPending)) => { let ticks = crate::files::tick_str(); (String::from(ticks), "") - }, - Err(_) => (String::from("ERR"), "") + } + Err(_) => (String::from("ERR"), ""), }; let (tag, tag_len) = match file.is_tagged() { Ok(true) => (Some(term::color_red() + "*"), 1), - _ => (None, 0) + _ => (None, 0), }; - let tag = tag.as_ref() - .map(|t| t.as_str()) - .unwrap_or(""); + let tag = tag.as_ref().map(|t| t.as_str()).unwrap_or(""); let selection_color = crate::term::color_yellow(); let (selection_gap, selection_color) = match file.is_selected() { true => (" ", selection_color.as_str()), - false => ("", "") + false => ("", ""), }; let (link_indicator, link_indicator_len) = match file.target { - Some(_) => (Some(format!("{}{}{}", - term::color_yellow(), - "--> ", - term::highlight_color())), Some(4)), - None => (None, None) + Some(_) => ( + Some(format!( + "{}{}{}", + term::color_yellow(), + "--> ", + term::highlight_color() + )), + Some(4), + ), + None => (None, None), }; - let link_indicator = link_indicator.as_ref() - .map(|l| l.as_str()) - .unwrap_or(""); + let link_indicator = link_indicator.as_ref().map(|l| l.as_str()).unwrap_or(""); let link_indicator_len = link_indicator_len.unwrap_or(0); let sized_string = term::sized_string(&name, xsize); let size = size.to_string(); - let size_pos = xsize - (size.len() as u16 + - unit.len() as u16 + - link_indicator_len as u16); + let size_pos = + xsize - (size.len() as u16 + unit.len() as u16 + link_indicator_len as u16); let padding = sized_string.len() - sized_string.width_cjk(); let padding = xsize - padding as u16; let padding = padding - tag_len; let padding = padding - icon.width() as u16; let padding = padding - icon_space.len() as u16; - let padding = padding - 1; write!(&mut line, "{}", termion::cursor::Save).unwrap(); match file.get_color() { - Some(color) => write!(&mut line, - "{}{}{}{}{}{}{:padding$}{}", - tag, - &color, - selection_color, - selection_gap, - icon, - icon_space, - &sized_string, - term::normal_color(), - padding = padding as usize), - _ => write!(&mut line, - "{}{}{}{}{}{}{:padding$}{}", - tag, - term::normal_color(), - selection_color, - selection_gap, - icon, - icon_space , - &sized_string, - term::normal_color(), - padding = padding as usize), - }.unwrap(); - - write!(&mut line, - "{}{}{}{}{}{}", - termion::cursor::Restore, - termion::cursor::Right(size_pos), - link_indicator, - term::highlight_color(), - size, - unit).unwrap(); - + Some(color) => write!( + &mut line, + "{}{}{}{}{}{}{:padding$}{}", + tag, + &color, + selection_color, + selection_gap, + icon, + icon_space, + &sized_string, + term::normal_color(), + padding = padding as usize + ), + _ => write!( + &mut line, + "{}{}{}{}{}{}{:padding$}{}", + tag, + term::normal_color(), + selection_color, + selection_gap, + icon, + icon_space, + &sized_string, + term::normal_color(), + padding = padding as usize + ), + } + .unwrap(); + + write!( + &mut line, + "{}{}{}{}{}{}", + termion::cursor::Restore, + termion::cursor::Right(size_pos), + link_indicator, + term::highlight_color(), + size, + unit + ) + .unwrap(); line } } - fn render(&self) -> Vec { let render_fn = self.render_line_fn(); let ysize = self.get_coordinates().unwrap().ysize_u(); @@ -913,7 +923,7 @@ impl ListView self.content .iter_files_from(selected_file, files_above_selection) - .take(ysize+1) + .take(ysize + 1) .map(|file| render_fn(file)) .collect() } @@ -935,10 +945,9 @@ impl ListView } } - impl Widget for ListView where - ListView: Listable + ListView: Listable, { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) diff --git a/src/main.rs b/src/main.rs index 1ce0d87..88e8ddc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,87 +1,79 @@ -#![feature(vec_remove_item)] -#![feature(trivial_bounds)] -#![feature(try_trait)] #![allow(dead_code)] extern crate termion; extern crate unicode_width; #[macro_use] extern crate lazy_static; +extern crate chrono; +extern crate clap; +extern crate dirs_2; extern crate failure; extern crate failure_derive; -extern crate natord; -extern crate dirs_2; -extern crate lscolors; -extern crate users; -extern crate chrono; -extern crate rayon; extern crate libc; +extern crate lscolors; +extern crate mime; +extern crate mime_guess; +extern crate natord; extern crate notify; extern crate parse_ansi; +extern crate rayon; extern crate signal_notify; -extern crate tree_magic_fork; -extern crate systemstat; -extern crate mime_guess; -extern crate mime; -extern crate clap; extern crate strum; +extern crate systemstat; +extern crate tree_magic_fork; +extern crate users; #[macro_use] extern crate strum_macros; #[macro_use] extern crate derivative; +extern crate crossbeam; extern crate nix; extern crate strip_ansi_escapes; -extern crate crossbeam; +extern crate async_value; extern crate osstrtools; extern crate pathbuftools; -extern crate async_value; -use failure::Fail; use clap::{App, Arg}; +use failure::Fail; use std::panic; +mod bookmarks; +mod config; +mod config_installer; mod coordinates; +mod dirty; +mod fail; mod file_browser; mod files; +mod foldview; +mod fscache; +mod hbox; +mod icon; +mod imgview; +mod keybind; mod listview; +mod mediaview; mod miller_columns; -mod preview; -mod term; -mod textview; -mod widget; -mod hbox; -mod tabview; -mod fail; mod minibuffer; -mod proclist; -mod bookmarks; mod paths; -mod foldview; -mod dirty; -mod fscache; -mod config; -mod stats; -mod icon; +mod preview; +mod proclist; mod quick_actions; +mod stats; +mod tabview; +mod term; +mod textview; mod trait_ext; -mod config_installer; -mod imgview; -mod mediaview; -mod keybind; - - - - +mod widget; -use widget::{Widget, WidgetCore}; -use term::ScreenExt; -use fail::{HResult, HError, MimeError, ErrorLog}; +use fail::{ErrorLog, HError, HResult, MimeError}; use file_browser::FileBrowser; use tabview::TabView; +use term::ScreenExt; use trait_ext::PathBufMime; - +use widget::{Widget, WidgetCore}; fn reset_screen(core: &mut WidgetCore) -> HResult<()> { core.screen.suspend() @@ -143,7 +135,6 @@ fn run(mut core: WidgetCore) -> HResult<()> { Ok(()) } - fn parse_args() -> clap::ArgMatches<'static> { App::new(clap::crate_name!()) .version(clap::crate_version!()) @@ -195,16 +186,12 @@ fn parse_args() -> clap::ArgMatches<'static> { .get_matches() } - - fn process_args(args: clap::ArgMatches, core: WidgetCore) { let path = args.value_of("path"); // Just print MIME and quit if args.is_present("mime") { - get_mime(path) - .map_err(|e| eprintln!("{}", e)). - ok(); + get_mime(path).map_err(|e| eprintln!("{}", e)).ok(); // If we get heres something went wrong. std::process::exit(1) } @@ -214,19 +201,12 @@ fn process_args(args: clap::ArgMatches, core: WidgetCore) { } if let Some(path) = path { - std::env::set_current_dir(&path) - .map_err(HError::from) - .log(); + std::env::set_current_dir(&path).map_err(HError::from).log(); } crate::config::set_argv_config(args).log(); } - - - - - fn get_mime(path: Option<&str>) -> HResult<()> { let path = path.ok_or(MimeError::NoFileProvided)?; let path = std::path::PathBuf::from(path); diff --git a/src/mediaview.rs b/src/mediaview.rs index e1dd7de..7a0cbf1 100644 --- a/src/mediaview.rs +++ b/src/mediaview.rs @@ -1,16 +1,18 @@ +use failure::{self, Fail}; use lazy_static; use termion::event::Key; -use failure::{self, Fail}; -use crate::widget::{Widget, WidgetCore}; -use crate::coordinates::Coordinates; use crate::async_value::Stale; -use crate::fail::{HResult, HError, ErrorLog, ErrorCause}; +use crate::coordinates::Coordinates; +use crate::fail::{ErrorCause, ErrorLog, HError, HResult}; use crate::imgview::ImgView; +use crate::widget::{Widget, WidgetCore}; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex, RwLock, - mpsc::{channel, Sender}}; +use std::sync::{ + mpsc::{channel, Sender}, + Arc, Mutex, RwLock, +}; use std::io::{BufRead, BufReader, Write}; use std::process::Child; @@ -56,33 +58,42 @@ pub struct MediaView { duration: Arc>, stale: Stale, process: Arc>>, - preview_runner: Option>, - Arc>, - Arc>) - -> HResult<()> + Send + 'static>> + preview_runner: Option< + Box< + dyn FnOnce( + bool, + bool, + Arc>, + Arc>, + Arc>, + ) -> HResult<()> + + Send + + 'static, + >, + >, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub enum MediaType { Video, - Audio + Audio, } impl MediaType { pub fn to_str(&self) -> &str { match self { MediaType::Video => "video", - MediaType::Audio => "audio" + MediaType::Audio => "audio", } } } impl MediaView { - pub fn new_from_file(core: WidgetCore, - file: &Path, - media_type: MediaType) -> HResult { + pub fn new_from_file( + core: WidgetCore, + file: &Path, + media_type: MediaType, + ) -> HResult { let imgview = ImgView { core: core.clone(), buffer: vec![], @@ -107,143 +118,143 @@ impl MediaView { let media_previewer = core.config().media_previewer; let g_mode = core.config().graphics; - let run_preview = Box::new(move | auto, - mute, - height: Arc>, - position: Arc>, - duration: Arc>| -> HResult<()> { - loop { - if tstale.is_stale()? { - return Ok(()); - } - - // Use current size. Widget could have been resized at some point - let (xsize, ysize, xpix, ypix) = - { - let view = thread_imgview.lock()?; - let (xsize, ysize) = view.core.coordinates.size_u(); - let (xpix, ypix) = view.core.coordinates.size_pixels()?; - (xsize, ysize, xpix, ypix) - }; - let cell_ratio = crate::term::cell_ratio()?; - - - let mut previewer = std::process::Command::new(&media_previewer) - .arg(format!("{}", (xsize+1))) - // Leave space for position/seek bar - .arg(format!("{}", (ysize-1))) - .arg(format!("{}", xpix)) - .arg(format!("{}", ypix)) - .arg(format!("{}", cell_ratio)) - .arg(format!("{}", ctype.to_str())) - .arg(format!("{}", auto)) - .arg(format!("{}", mute)) - .arg(format!("{}", g_mode)) - .arg(&path) - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::null()) - .spawn() - .map_err(|e| { - let msg = format!("Couldn't run {}{}{}! Error: {:?}", - crate::term::color_red(), - media_previewer, - crate::term::normal_color(), - &e.kind()); - - ccore.show_status(&msg).log(); - - MediaError::NoPreviewer(msg) - })?; - - let mut stdout = BufReader::new(previewer.stdout.take()?); - let mut stdin = previewer.stdin.take()?; - - cprocess.lock().map(|mut p| *p = Some(previewer))?; - - let mut frame = vec![]; - let newline = String::from("\n"); - let mut line_buf = String::new(); - let rx_cmd = rx_cmd.clone(); - - std::thread::spawn(move || -> HResult<()> { - for cmd in rx_cmd.lock()?.iter() { - write!(stdin, "{}", cmd)?; - write!(stdin, "\n")?; - stdin.flush()?; - } - Ok(()) - }); - + let run_preview = Box::new( + move |auto, + mute, + height: Arc>, + position: Arc>, + duration: Arc>| + -> HResult<()> { loop { - // Check if preview-gen finished and break out of loop to restart - if let Ok(Some(code)) = cprocess.lock()? - .as_mut()? - .try_wait() { - if code.success() { - break; - } else { - let msg = String::from("hunter-media failed!"); - return Err(failure::format_err!("{}", msg))?; - } + if tstale.is_stale()? { + return Ok(()); } - - stdout.read_line(&mut line_buf)?; - - - // Newline means frame is complete - if line_buf == newline { - let new_height; - - line_buf.clear(); - stdout.read_line(&mut line_buf)?; - let h = line_buf.trim().parse::()?; - - let mut height = height.lock().unwrap(); - if *height != h { - new_height = true; - } else { - new_height = false; + // Use current size. Widget could have been resized at some point + let (xsize, ysize, xpix, ypix) = { + let view = thread_imgview.lock()?; + let (xsize, ysize) = view.core.coordinates.size_u(); + let (xpix, ypix) = view.core.coordinates.size_pixels()?; + (xsize, ysize, xpix, ypix) + }; + let cell_ratio = crate::term::cell_ratio()?; + + let mut previewer = std::process::Command::new(&media_previewer) + .arg(format!("{}", (xsize + 1))) + // Leave space for position/seek bar + .arg(format!("{}", (ysize - 1))) + .arg(format!("{}", xpix)) + .arg(format!("{}", ypix)) + .arg(format!("{}", cell_ratio)) + .arg(format!("{}", ctype.to_str())) + .arg(format!("{}", auto)) + .arg(format!("{}", mute)) + .arg(format!("{}", g_mode)) + .arg(&path) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::null()) + .spawn() + .map_err(|e| { + let msg = format!( + "Couldn't run {}{}{}! Error: {:?}", + crate::term::color_red(), + media_previewer, + crate::term::normal_color(), + &e.kind() + ); + + ccore.show_status(&msg).log(); + + MediaError::NoPreviewer(msg) + })?; + + let mut stdout = + BufReader::new(previewer.stdout.take().ok_or_else(|| HError::NoneError)?); + let mut stdin = previewer.stdin.take().ok_or_else(|| HError::NoneError)?; + + cprocess.lock().map(|mut p| *p = Some(previewer))?; + + let mut frame = vec![]; + let newline = String::from("\n"); + let mut line_buf = String::new(); + let rx_cmd = rx_cmd.clone(); + + std::thread::spawn(move || -> HResult<()> { + for cmd in rx_cmd.lock()?.iter() { + write!(stdin, "{}", cmd)?; + write!(stdin, "\n")?; + stdin.flush()?; + } + Ok(()) + }); + + loop { + // Check if preview-gen finished and break out of loop to restart + if let Ok(Some(code)) = cprocess + .lock()? + .as_mut() + .ok_or_else(|| HError::NoneError)? + .try_wait() + { + if code.success() { + break; + } else { + let msg = String::from("hunter-media failed!"); + return Err(failure::format_err!("{}", msg))?; + } } - *height = h; - - line_buf.clear(); stdout.read_line(&mut line_buf)?; - let pos = &line_buf.trim(); - *position.lock().unwrap() = pos - .parse::()?; + // Newline means frame is complete + if line_buf == newline { + let new_height; - line_buf.clear(); - stdout.read_line(&mut line_buf)?; - let dur = &line_buf.trim(); - *duration.lock().unwrap() = dur - .parse::()?; + line_buf.clear(); + stdout.read_line(&mut line_buf)?; + let h = line_buf.trim().parse::()?; - - if let Ok(mut imgview) = thread_imgview.lock() { - if new_height { - imgview.core.clear()?; + let mut height = height.lock().unwrap(); + if *height != h { + new_height = true; + } else { + new_height = false; + } + *height = h; + + line_buf.clear(); + stdout.read_line(&mut line_buf)?; + let pos = &line_buf.trim(); + *position.lock().unwrap() = pos.parse::()?; + + line_buf.clear(); + stdout.read_line(&mut line_buf)?; + let dur = &line_buf.trim(); + *duration.lock().unwrap() = dur.parse::()?; + + if let Ok(mut imgview) = thread_imgview.lock() { + if new_height { + imgview.core.clear()?; + } + imgview.set_image_data(frame); + sender + .send(crate::widget::Events::WidgetReady) + .map_err(|e| HError::from(e)) + .log(); } - imgview.set_image_data(frame); - sender.send(crate::widget::Events::WidgetReady) - .map_err(|e| HError::from(e)) - .log(); - } - line_buf.clear(); - frame = vec![]; - continue; - } else { - frame.push(line_buf); - line_buf = String::new(); + line_buf.clear(); + frame = vec![]; + continue; + } else { + frame.push(line_buf); + line_buf = String::new(); + } } } - } - }); - + }, + ); Ok(MediaView { core: core.clone(), @@ -257,7 +268,7 @@ impl MediaView { duration: Arc::new(Mutex::new(0)), stale: stale, process: process, - preview_runner: Some(run_preview) + preview_runner: Some(run_preview), }) } @@ -281,11 +292,7 @@ impl MediaView { if !stale.is_stale()? { print!("{}", clear); - runner.map(|runner| runner(autoplay, - mute, - height, - position, - duration).log()); + runner.map(|runner| runner(autoplay, mute, height, position, duration).log()); } Ok(()) }); @@ -298,7 +305,7 @@ impl MediaView { } pub fn pause(&self) -> HResult<()> { - Ok(self.controller.send(String::from ("a"))?) + Ok(self.controller.send(String::from("a"))?) } pub fn progress_bar(&self) -> HResult { @@ -308,17 +315,19 @@ impl MediaView { let duration = self.duration.lock()?.clone(); if duration == 0 || position == 0 { - Ok(format!("{:elements$}", "|", elements=xsize)) + Ok(format!("{:elements$}", "|", elements = xsize)) } else { let element_percent = 100 as f32 / xsize as f32; let progress_percent = position as f32 / duration as f32 * 100 as f32; let element_count = progress_percent as f32 / element_percent as f32; - Ok(format!("{:|>elements$}|{: >empty$}", - "", - "", - empty=xsize - (element_count as usize), - elements=element_count as usize)) + Ok(format!( + "{:|>elements$}|{: >empty$}", + "", + "", + empty = xsize - (element_count as usize), + elements = element_count as usize + )) } } @@ -343,19 +352,19 @@ impl MediaView { let mut icons = String::new(); if *MUTE.read()? == true { - icons += &crate::term::goto_xy_u(xpos+xsize-2, ypos+lines); + icons += &crate::term::goto_xy_u(xpos + xsize - 2, ypos + lines); icons += mute_char; } else { // Clear the mute symbol, or it doesn't go away - icons += &crate::term::goto_xy_u(xpos+xsize-2, ypos+lines); + icons += &crate::term::goto_xy_u(xpos + xsize - 2, ypos + lines); icons += " "; } if *AUTOPLAY.read()? == true { - icons += &crate::term::goto_xy_u(xpos+xsize-4, ypos+lines); + icons += &crate::term::goto_xy_u(xpos + xsize - 4, ypos + lines); icons += play_char; } else { - icons += &crate::term::goto_xy_u(xpos+xsize-4, ypos+lines); + icons += &crate::term::goto_xy_u(xpos + xsize - 4, ypos + lines); icons += pause_char; } @@ -363,9 +372,8 @@ impl MediaView { } pub fn format_secs(&self, secs: usize) -> String { - let hours = if secs >= 60*60 { (secs / 60) / 60 } else { 0 }; - let mins = if secs >= 60 { (secs / 60) %60 } else { 0 }; - + let hours = if secs >= 60 * 60 { (secs / 60) / 60 } else { 0 }; + let mins = if secs >= 60 { (secs / 60) % 60 } else { 0 }; format!("{:02}:{:02}:{:02}", hours, mins, secs % 60) } @@ -383,7 +391,7 @@ impl MediaView { self.paused = false; self.play()?; - return Ok(()) + return Ok(()); } if self.paused { self.toggle_autoplay(); @@ -443,12 +451,10 @@ impl MediaView { pub fn kill(&mut self) -> HResult<()> { let proc = self.process.clone(); std::thread::spawn(move || -> HResult<()> { - proc.lock()? - .as_mut() - .map(|p| { - p.kill().map_err(|e| HError::from(e)).log(); - p.wait().map_err(|e| HError::from(e)).log(); - }); + proc.lock()?.as_mut().map(|p| { + p.kill().map_err(|e| HError::from(e)).log(); + p.wait().map_err(|e| HError::from(e)).log(); + }); Ok(()) }); Ok(()) @@ -465,8 +471,9 @@ impl Widget for MediaView { } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { - if &self.core.coordinates == coordinates { return Ok(()); } - + if &self.core.coordinates == coordinates { + return Ok(()); + } self.core.coordinates = coordinates.clone(); @@ -478,12 +485,14 @@ impl Widget for MediaView { let (xpix, ypix) = self.core.coordinates.size_pixels()?; let cell_ratio = crate::term::cell_ratio()?; - let xystring = format!("xy\n{}\n{}\n{}\n{}\n{}\n", - xsize+1, - ysize-1, - xpix, - ypix, - cell_ratio); + let xystring = format!( + "xy\n{}\n{}\n{}\n{}\n{}\n", + xsize + 1, + ysize - 1, + xpix, + ypix, + cell_ratio + ); self.controller.send(xystring)?; @@ -501,16 +510,14 @@ impl Widget for MediaView { let progress_str = self.progress_string()?; let progress_bar = self.progress_bar()?; - let frame= self.imgview - .lock() - .map(|img| img.get_drawlist())?; + let frame = self.imgview.lock().map(|img| img.get_drawlist())?; let mut frame = frame?; - frame += &crate::term::goto_xy_u(xpos, ypos+height); + frame += &crate::term::goto_xy_u(xpos, ypos + height); frame += &progress_str; frame += &self.get_icons(height)?; - frame += &crate::term::goto_xy_u(xpos, ypos+height+1); + frame += &crate::term::goto_xy_u(xpos, ypos + height + 1); frame += &progress_bar; Ok(frame) @@ -530,7 +537,6 @@ impl Drop for MediaView { } } - use crate::keybind::*; impl Acting for MediaView { diff --git a/src/minibuffer.rs b/src/minibuffer.rs index de0ca91..269f20d 100644 --- a/src/minibuffer.rs +++ b/src/minibuffer.rs @@ -3,10 +3,10 @@ use termion::event::Key; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; -use crate::coordinates::{Coordinates}; -use crate::widget::{Widget, WidgetCore}; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::coordinates::Coordinates; +use crate::fail::{ErrorLog, HError, HResult}; use crate::term::ScreenExt; +use crate::widget::{Widget, WidgetCore}; type HMap = HashMap>; @@ -14,7 +14,7 @@ type HMap = HashMap>; struct History { history: HMap, position: Option, - loaded: bool + loaded: bool, } impl History { @@ -22,31 +22,35 @@ impl History { History { history: HashMap::new(), position: None, - loaded: false + loaded: false, } } fn load(&mut self) -> HResult<()> { - if self.loaded { return Ok(()) } + if self.loaded { + return Ok(()); + } let hpath = crate::paths::history_path()?; let hf_content = std::fs::read_to_string(hpath)?; - let history = hf_content.lines().fold(HashMap::new(), |mut hm: HMap, line| { - let parts = line.splitn(2, ":").collect::>(); - if parts.len() == 2 { - let (htype, hline) = (parts[0].to_string(), parts[1].to_string()); - - match hm.get_mut(&htype) { - Some(hvec) => hvec.push(hline), - None => { - let hvec = vec![hline]; - hm.insert(htype, hvec); - } - }; - } - hm - }); + let history = hf_content + .lines() + .fold(HashMap::new(), |mut hm: HMap, line| { + let parts = line.splitn(2, ":").collect::>(); + if parts.len() == 2 { + let (htype, hline) = (parts[0].to_string(), parts[1].to_string()); + + match hm.get_mut(&htype) { + Some(hvec) => hvec.push(hline), + None => { + let hvec = vec![hline]; + hm.insert(htype, hvec); + } + }; + } + hm + }); self.history = history; self.loaded = true; @@ -57,10 +61,16 @@ impl History { fn save(&self) -> HResult<()> { let hpath = crate::paths::history_path()?; - let history = self.history.iter().map(|(htype, hlines)| { - hlines.iter().map(|hline| format!("{}:{}\n", htype, hline)) - .collect::() - }).collect::(); + let history = self + .history + .iter() + .map(|(htype, hlines)| { + hlines + .iter() + .map(|hline| format!("{}:{}\n", htype, hline)) + .collect::() + }) + .collect::(); std::fs::write(hpath, history)?; Ok(()) @@ -86,12 +96,16 @@ impl History { fn get_prev(&mut self, htype: &str) -> HResult { self.load()?; - let history = self.history.get(htype)?; + let history = self.history.get(htype).ok_or_else(|| HError::NoneError)?; let mut position = self.position; let hist_len = history.len(); - if position == Some(0) { position = None; } - if hist_len == 0 { return Err(HError::NoHistoryError); } + if position == Some(0) { + position = None; + } + if hist_len == 0 { + return Err(HError::NoHistoryError); + } if let Some(position) = position { let historic = history[position - 1].clone(); @@ -102,19 +116,20 @@ impl History { self.position = Some(hist_len - 1); Ok(historic) } - } fn get_next(&mut self, htype: &str) -> HResult { self.load()?; - let history = self.history.get(htype)?; + let history = self.history.get(htype).ok_or_else(|| HError::NoneError)?; let mut position = self.position; let hist_len = history.len(); - if hist_len == 0 { return Err(HError::NoHistoryError); } - if position == Some(hist_len) || - position == None - { position = Some(0); } + if hist_len == 0 { + return Err(HError::NoHistoryError); + } + if position == Some(hist_len) || position == None { + position = Some(0); + } if let Some(position) = position { let historic = history[position].clone(); @@ -135,7 +150,7 @@ pub enum MiniBufferEvent { Empty, Cancelled, CycleNext, - CyclePrev + CyclePrev, } #[derive(Debug)] @@ -147,7 +162,7 @@ pub struct MiniBuffer { history: History, completions: Vec, last_completion: Option, - continuous: bool + continuous: bool, } impl MiniBuffer { @@ -165,7 +180,7 @@ impl MiniBuffer { history: History::new(), completions: vec![], last_completion: None, - continuous: false + continuous: false, } } @@ -188,7 +203,8 @@ impl MiniBuffer { if self.input == "" { self.clear(); - self.input_empty()?; } + self.input_empty()?; + } Ok(self.input.clone()) } @@ -207,7 +223,8 @@ impl MiniBuffer { return Ok(()); } - let part = self.input + let part = self + .input .rsplitn(2, " ") .take(1) .map(|s| s.to_string()) @@ -215,11 +232,10 @@ impl MiniBuffer { let completions = find_files(&part); if let Ok(mut completions) = completions { - let completion = completions.pop()?; + let completion = completions.pop().ok_or_else(|| HError::NoneError)?; let completion = completion.to_string_lossy(); - self.input - = self.input[..self.input.len() - part.len()].to_string(); + self.input = self.input[..self.input.len() - part.len()].to_string(); self.input.push_str(&completion); self.position += &completion.len() - part.len(); @@ -229,11 +245,10 @@ impl MiniBuffer { let completions = find_bins(&part); if let Ok(mut completions) = completions { - let completion = completions.pop()?; + let completion = completions.pop().ok_or_else(|| HError::NoneError)?; let completion = completion.to_string_lossy(); - self.input = self.input[..self.input.len() - - part.len()].to_string(); + self.input = self.input[..self.input.len() - part.len()].to_string(); self.input.push_str(&completion); self.position += &completion.len() - part.len(); @@ -249,13 +264,16 @@ impl MiniBuffer { } pub fn cycle_completions(&mut self) -> HResult<()> { - let last_comp = self.last_completion.as_ref()?; + let last_comp = self + .last_completion + .as_ref() + .ok_or_else(|| HError::NoneError)?; let last_len = last_comp.len(); self.input = self.input.trim_end_matches(last_comp).to_string(); self.position = self.position.saturating_sub(last_len); - let next_comp = self.completions.pop()?; + let next_comp = self.completions.pop().ok_or_else(|| HError::NoneError)?; let next_comp = next_comp.to_string_lossy(); self.input.push_str(&next_comp); self.position += next_comp.len(); @@ -268,7 +286,6 @@ impl MiniBuffer { return Err(MiniBufferEvent::CyclePrev)?; } - if let Ok(historic) = self.history.get_prev(&self.query) { self.position = historic.len(); self.input = historic; @@ -281,7 +298,6 @@ impl MiniBuffer { return Err(MiniBufferEvent::CycleNext)?; } - if let Ok(historic) = self.history.get_next(&self.query) { self.position = historic.len(); self.input = historic; @@ -323,26 +339,16 @@ impl MiniBuffer { let new_input = match boundaries { (Some(dir_boundary), Some(word_boundary)) => { if dir_boundary > word_boundary { - before_cursor - .split_at(dir_boundary).0 - .to_string() + "/" + before_cursor.split_at(dir_boundary).0.to_string() + "/" } else { - before_cursor - .split_at(word_boundary).0 - .to_string() + " " + before_cursor.split_at(word_boundary).0.to_string() + " " } } - (Some(dir_boundary), None) => { - before_cursor - .split_at(dir_boundary).0 - .to_string() + "/" - } + (Some(dir_boundary), None) => before_cursor.split_at(dir_boundary).0.to_string() + "/", (None, Some(word_boundary)) => { - before_cursor - .split_at(word_boundary).0 - .to_string() + " " + before_cursor.split_at(word_boundary).0.to_string() + " " } - (None, None) => "".to_string() + (None, None) => "".to_string(), } + after_cursor; let len_difference = old_input_len - new_input.len(); @@ -354,7 +360,7 @@ impl MiniBuffer { } pub fn input_finnished(&self) -> HResult<()> { - return HError::popup_finnished() + return HError::popup_finnished(); } pub fn input_cancelled(&self) -> HResult<()> { @@ -375,31 +381,35 @@ impl MiniBuffer { pub fn find_bins(comp_name: &str) -> HResult> { use osstrtools::OsStrTools; - let paths = std::env::var_os("PATH")?; + let paths = std::env::var_os("PATH").ok_or_else(|| HError::NoneError)?; let paths = paths.split(":"); - let completions = paths.iter().map(|path| { - std::fs::read_dir(path).map(|read_dir| { - read_dir.map(|file| { - let file = file?; - let name = file.file_name(); - - // If length is different that means the file starts with comp_name - if &name.trim_start(comp_name).len() != &name.len() { - Ok(name) - } else { - Err(HError::NoCompletionsError) - } - + let completions = paths + .iter() + .map(|path| { + std::fs::read_dir(path).map(|read_dir| { + read_dir.map(|file| { + let file = file?; + let name = file.file_name(); + + // If length is different that means the file starts with comp_name + if &name.trim_start(comp_name).len() != &name.len() { + Ok(name) + } else { + Err(HError::NoCompletionsError) + } + }) }) }) - }).flatten() - .flatten() - .filter(|s| s.is_ok()) - .map(|s| s.unwrap()) - .collect::>(); - - if completions.is_empty() { return Err(HError::NoCompletionsError); } + .flatten() + .flatten() + .filter(|s| s.is_ok()) + .map(|s| s.unwrap()) + .collect::>(); + + if completions.is_empty() { + return Err(HError::NoCompletionsError); + } Ok(completions) } @@ -413,47 +423,55 @@ pub fn find_files(comp_name: &str) -> HResult> { // Tried to complete on an incorrect path if comp_name.ends_with("/") && !path.is_dir() { - return Err(HError::NoCompletionsError) + return Err(HError::NoCompletionsError); } let comp_name = OsStr::new(comp_name); - let filename_part = path.file_name()?; + let filename_part = path.file_name().ok_or_else(|| HError::NoneError)?; - let dir = if path.is_dir() { &path } else { path.parent()? }; + let dir = if path.is_dir() { + &path + } else { + path.parent().ok_or_else(|| HError::NoneError)? + }; let dir = std::path::PathBuf::from(dir); let prefix = comp_name.trim_end(&filename_part); let reader = std::fs::read_dir(&dir)?; - let completions = reader.map(|file| { - let file = file?; - let name = file.file_name(); - if name.trim_start(&filename_part).len() != name.len() { - let mut completion = OsString::new(); - if file.file_type()?.is_dir() { - completion.push(prefix.trim_end("/")); + let completions = reader + .map(|file| { + let file = file?; + let name = file.file_name(); + if name.trim_start(&filename_part).len() != name.len() { + let mut completion = OsString::new(); + if file.file_type()?.is_dir() { + completion.push(prefix.trim_end("/")); + + // When completing something in the curren dir this will be empty + if completion != "" { + completion.push("/"); + } + completion.push(name); - // When completing something in the curren dir this will be empty - if completion != "" { + // Add final slash to directory completion.push("/"); + Ok(completion) + } else { + completion.push(prefix); + completion.push(name); + Ok(completion) } - completion.push(name); - - // Add final slash to directory - completion.push("/"); - Ok(completion) } else { - completion.push(prefix); - completion.push(name); - Ok(completion) + Err(HError::NoCompletionsError) } - } else { - Err(HError::NoCompletionsError) - } - }).filter_map(|res| res.ok()) - .collect::>(); - if completions.is_empty() { return Err(HError::NoCompletionsError); } + }) + .filter_map(|res| res.ok()) + .collect::>(); + if completions.is_empty() { + return Err(HError::NoCompletionsError); + } Ok(completions) } @@ -470,12 +488,14 @@ impl Widget for MiniBuffer { fn get_drawlist(&self) -> HResult { let (xpos, ypos) = self.get_coordinates()?.u16position(); - Ok(format!("{}{}{}{}: {}", - crate::term::goto_xy(xpos, ypos), - termion::clear::CurrentLine, - crate::term::header_color(), - self.query, - self.input)) + Ok(format!( + "{}{}{}{}: {}", + crate::term::goto_xy(xpos, ypos), + termion::clear::CurrentLine, + crate::term::header_color(), + self.query, + self.input + )) } fn on_key(&mut self, key: Key) -> HResult<()> { @@ -491,9 +511,7 @@ impl Widget for MiniBuffer { } fn after_draw(&self) -> HResult<()> { - let cursor_pos = crate::term::string_len(&self.query) + - ": ".len() + - self.position; + let cursor_pos = crate::term::string_len(&self.query) + ": ".len() + self.position; let mut screen = self.core.screen()?; let ysize = screen.ysize()?; @@ -523,23 +541,26 @@ impl Acting for MiniBuffer { self.position += 1; } InsertTab(n) => { - let fnstr = format!("${}", n-1); + let fnstr = format!("${}", n - 1); self.input.insert_str(self.position, &fnstr); self.position += 2; } - Cancel => { self.clear(); self.input_cancelled()? } + Cancel => { + self.clear(); + self.input_cancelled()? + } Finish => { if self.input != "" { self.history.add(&self.query, &self.input); } self.input_finnished()? - }, + } Complete => self.complete()?, DeleteChar => { if self.position != self.input.len() { self.input.remove(self.position); } - }, + } BackwardDeleteChar => { if self.position != 0 { self.input.remove(self.position - 1); @@ -550,12 +571,12 @@ impl Acting for MiniBuffer { if self.position != 0 { self.position -= 1; } - }, + } CursorRight => { if self.position != self.input.len() { self.position += 1; } - }, + } HistoryUp => self.history_up()?, HistoryDown => self.history_down()?, ClearLine => self.clear_line()?, diff --git a/src/paths.rs b/src/paths.rs index 24336e8..14e5133 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -2,22 +2,22 @@ use dirs_2; use std::path::PathBuf; -use crate::fail::HResult; +use crate::fail::{HError, HResult}; pub fn home_path() -> HResult { - let home = dirs_2::home_dir()?; + let home = dirs_2::home_dir().ok_or_else(|| HError::NoneError)?; Ok(home) } pub fn ranger_path() -> HResult { - let mut ranger_path = dirs_2::config_dir()?; + let mut ranger_path = dirs_2::config_dir().ok_or_else(|| HError::NoneError)?; ranger_path.push("ranger/"); Ok(ranger_path) } #[cfg(not(target_os = "macos"))] pub fn hunter_path() -> HResult { - let mut hunter_path = dirs_2::config_dir()?; + let mut hunter_path = dirs_2::config_dir().ok_or_else(|| HError::NoneError)?; hunter_path.push("hunter/"); Ok(hunter_path) } diff --git a/src/preview.rs b/src/preview.rs index 382fdf2..1154714 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -1,22 +1,20 @@ use async_value::{Async, Stale}; use termion::event::Key; -use std::sync::{Arc, Mutex}; use std::path::PathBuf; +use std::sync::{Arc, Mutex}; -use crate::files::{File, Files, Kind, Ticker}; -use crate::fscache::FsCache; -use crate::listview::{ListView, FileSource}; -use crate::textview::TextView; -use crate::widget::{Widget, WidgetCore}; use crate::coordinates::Coordinates; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult}; +use crate::files::{File, Files, Kind}; +use crate::fscache::FsCache; use crate::imgview::ImgView; +use crate::listview::{FileSource, ListView}; use crate::mediaview::MediaView; +use crate::textview::TextView; +use crate::widget::{Widget, WidgetCore}; - -pub type AsyncWidgetFn = dyn FnOnce(&Stale, WidgetCore) - -> HResult + Send + Sync; +pub type AsyncWidgetFn = dyn FnOnce(&Stale, WidgetCore) -> HResult + Send + Sync; lazy_static! { static ref SUBPROC: Arc>> = Arc::new(Mutex::new(None)); @@ -40,19 +38,14 @@ fn kill_proc() -> HResult<()> { killpg(pid, Signal::SIGTERM).ok(); std::thread::sleep(sleep_time); killpg(pid, Signal::SIGKILL).ok(); - }) - ); + })); *pid = None; Ok(()) } - - - impl PartialEq for AsyncWidget { fn eq(&self, other: &AsyncWidget) -> bool { - if self.get_coordinates().unwrap() == - other.get_coordinates().unwrap() { + if self.get_coordinates().unwrap() == other.get_coordinates().unwrap() { true } else { false @@ -63,53 +56,62 @@ impl PartialEq for AsyncWidget { #[derive(Debug)] pub struct AsyncWidget { pub widget: Async, - core: WidgetCore + core: WidgetCore, } impl AsyncWidget { - pub fn new(core: &WidgetCore, - closure: impl FnOnce(&Stale) -> HResult + Send + 'static) - -> AsyncWidget { + pub fn new( + core: &WidgetCore, + closure: impl FnOnce(&Stale) -> HResult + Send + 'static, + ) -> AsyncWidget { let sender = Arc::new(Mutex::new(core.get_sender())); - let mut widget = Async::new(move |stale| - closure(stale).map_err(|e| e.into())); - widget.on_ready(move |_, stale| { - if !stale.is_stale()? { - sender.lock().map(|s| s.send(crate::widget::Events::WidgetReady)).ok(); - } - Ok(()) - }).log(); + let mut widget = Async::new(move |stale| closure(stale).map_err(|e| e.into())); + widget + .on_ready(move |_, stale| { + crate::files::stop_ticking(); + if !stale.is_stale()? { + sender + .lock() + .map(|s| s.send(crate::widget::Events::WidgetReady)) + .ok(); + } + Ok(()) + }) + .log(); + crate::files::start_ticking(core.get_sender()); widget.run().log(); AsyncWidget { widget: widget, - core: core.clone() + core: core.clone(), } } - pub fn change_to(&mut self, - closure: impl FnOnce(&Stale, - WidgetCore) - -> HResult + Send + 'static) - -> HResult<()> { + pub fn change_to( + &mut self, + closure: impl FnOnce(&Stale, WidgetCore) -> HResult + Send + 'static, + ) -> HResult<()> { self.set_stale().log(); let sender = Mutex::new(self.get_core()?.get_sender()); let core = self.get_core()?.clone(); - let mut widget = Async::new(move |stale| { - Ok(closure(stale, core.clone())?) - }); + let mut widget = Async::new(move |stale| Ok(closure(stale, core.clone())?)); - widget.on_ready(move |_, stale| { - if !stale.is_stale()? { - sender.lock() - .map(|s| s.send(crate::widget::Events::WidgetReady)) - .ok(); - } - Ok(()) - }).log(); + widget + .on_ready(move |_, stale| { + crate::files::stop_ticking(); + if !stale.is_stale()? { + sender + .lock() + .map(|s| s.send(crate::widget::Events::WidgetReady)) + .ok(); + } + Ok(()) + }) + .log(); + crate::files::start_ticking(self.core.get_sender()); widget.run().log(); self.widget = widget; @@ -145,8 +147,6 @@ impl AsyncWidget { } } - - impl Widget for AsyncWidget { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) @@ -182,30 +182,32 @@ impl Widget for AsyncWidget { let clear = self.core.get_clearlist()?; let (xpos, ypos) = self.get_coordinates()?.u16position(); let pos = crate::term::goto_xy(xpos, ypos); - return Ok(clear + &pos + crate::files::tick_str()) + return Ok(clear + &pos + crate::files::tick_str()); } if self.is_stale()? { - return self.core.get_clearlist() + return self.core.get_clearlist(); } self.widget()?.get_drawlist() } fn on_key(&mut self, key: termion::event::Key) -> HResult<()> { - if self.widget().is_err() { return Ok(()) } + if self.widget().is_err() { + return Ok(()); + } self.widget_mut()?.on_key(key) } fn render_footer(&self) -> HResult { - if self.widget().is_err() { return Ok(String::new()) } + if self.widget().is_err() { + return Ok(String::new()); + } self.widget()?.render_footer() } } - impl PartialEq for Previewer { fn eq(&self, other: &Previewer) -> bool { - if self.widget.get_coordinates().unwrap() == - other.widget.get_coordinates().unwrap() { + if self.widget.get_coordinates().unwrap() == other.widget.get_coordinates().unwrap() { true } else { false @@ -218,27 +220,31 @@ enum PreviewWidget { FileList(ListView), TextView(TextView), ImgView(ImgView), - MediaView(MediaView) + MediaView(MediaView), } enum ExtPreviewer { Text(PathBuf), - Graphics(PathBuf) + Graphics(PathBuf), } fn find_previewer(file: &File, g_mode: bool) -> HResult { let path = crate::paths::previewers_path()?; - let ext = file.path.extension()?; + let ext = file.path.extension().ok_or_else(|| HError::NoneError)?; // Try to find a graphical previewer first if g_mode { - let g_previewer = path.read_dir()? - .find(|previewer| previewer.as_ref() - .and_then(|p| { - Ok(p.path().file_stem() == Some(ext) - && p.path().extension() == Some(&std::ffi::OsStr::new("g"))) - }) - .unwrap_or(false)) + let g_previewer = path + .read_dir()? + .find(|previewer| { + previewer + .as_ref() + .and_then(|p| { + Ok(p.path().file_stem() == Some(ext) + && p.path().extension() == Some(&std::ffi::OsStr::new("g"))) + }) + .unwrap_or(false) + }) .map(|p| p.map(|p| p.path())); match g_previewer { Some(Ok(g_p)) => return Ok(ExtPreviewer::Graphics(g_p)), @@ -246,14 +252,16 @@ fn find_previewer(file: &File, g_mode: bool) -> HResult { } } - - // Look for previewers matching the file extension - let previewer = path.read_dir()? - .find(|previewer| previewer.as_ref() - .and_then(|p| Ok(p.file_name() == ext )) - .unwrap_or(false)) - .map(|p| p.map(|p| p.path())); + let previewer = path + .read_dir()? + .find(|previewer| { + previewer + .as_ref() + .and_then(|p| Ok(p.file_name() == ext)) + .unwrap_or(false) + }) + .map(|p| p.map(|p| p.path())); match previewer { Some(Ok(p)) => return Ok(ExtPreviewer::Text(p)), _ => { @@ -267,19 +275,19 @@ fn find_previewer(file: &File, g_mode: bool) -> HResult { } } - Ok(ExtPreviewer::Text(previewer??)) + Ok(ExtPreviewer::Text( + previewer.ok_or_else(|| HError::NoneError)??, + )) } - pub struct Previewer { widget: AsyncWidget, core: WidgetCore, file: Option, pub cache: FsCache, - animator: Stale + animator: Stale, } - impl Previewer { pub fn new(core: &WidgetCore, cache: FsCache) -> Previewer { let core_ = core.clone(); @@ -289,18 +297,18 @@ impl Previewer { Ok(blank) }); - - Previewer { widget: widget, - core: core.clone(), - file: None, - cache: cache, - animator: Stale::new()} + Previewer { + widget: widget, + core: core.clone(), + file: None, + cache: cache, + animator: Stale::new(), + } } - fn become_preview(&mut self, - widget: HResult>) -> HResult<()> { + fn become_preview(&mut self, widget: HResult>) -> HResult<()> { let coordinates = self.get_coordinates()?.clone(); - self.widget = widget?; + self.widget = widget?; self.widget.set_coordinates(&coordinates)?; Ok(()) } @@ -324,37 +332,39 @@ impl Previewer { let files = std::mem::take(&mut file_list.content); Ok(files) } - _ => HError::no_files()? + _ => HError::no_files()?, } } - pub fn put_preview_files(&mut self, - files: Files, - selected_file: Option) { + pub fn put_preview_files(&mut self, files: Files, selected_file: Option) { let dir = files.directory.clone(); let cache = self.cache.clone(); self.file = Some(dir); - self.widget.change_to(move |stale, core| { - let source = crate::listview::FileSource::Files(files); + self.widget + .change_to(move |stale, core| { + let source = crate::listview::FileSource::Files(files); - let list = ListView::builder(core.clone(), source) - // .prerender() - .with_cache(cache) - .with_stale(stale.clone()) - .select(selected_file) - .build()?; + let list = ListView::builder(core.clone(), source) + // .prerender() + .with_cache(cache) + .with_stale(stale.clone()) + .select(selected_file) + .build()?; - Ok(PreviewWidget::FileList(list)) - }).log(); + Ok(PreviewWidget::FileList(list)) + }) + .log(); } - pub fn set_file(&mut self, - file: &File) -> HResult<()> { - if Some(file) == self.file.as_ref() && !self.widget.is_stale()? { return Ok(()) } + pub fn set_file(&mut self, file: &File) -> HResult<()> { + if Some(file) == self.file.as_ref() && !self.widget.is_stale()? { + return Ok(()); + } self.widget.set_stale().ok(); - let same_dir = self.file + let same_dir = self + .file .as_ref() .map(|f| f.path.parent() == file.path.parent()) .unwrap_or(true); @@ -372,82 +382,60 @@ impl Previewer { self.animator.set_stale().ok(); } - self.become_preview(Ok(AsyncWidget::new( - &self.core, - move |stale: &Stale| - { - kill_proc().log(); - // Delete files left by graphical PDF previews, etc. - if std::path::Path::new("/tmp/hunter-previews").exists() { - std::fs::remove_dir_all("/tmp/hunter-previews/") - .map_err(HError::from) - .log(); - } + self.become_preview(Ok(AsyncWidget::new(&self.core, move |stale: &Stale| { + kill_proc().log(); + // Delete files left by graphical PDF previews, etc. + if std::path::Path::new("/tmp/hunter-previews").exists() { + std::fs::remove_dir_all("/tmp/hunter-previews/") + .map_err(HError::from) + .log(); + } - if file.kind == Kind::Directory { - let preview = Previewer::preview_dir(&file, - cache, - &core, - &stale, - &animator); - return Ok(preview?); - } + if file.kind == Kind::Directory { + let preview = Previewer::preview_dir(&file, cache, &core, &stale, &animator); + return Ok(preview?); + } - if let Some(mime) = file.get_mime() - .log_and() - .ok() - { - let mime_type = mime.type_().as_str(); - let is_gif = mime.subtype() == "gif"; - let has_media = core.config().media_available(); - - match mime_type { - _ if mime_type == "video" || is_gif && has_media => { - let media_type = crate::mediaview::MediaType::Video; - let mediaview = MediaView::new_from_file(core.clone(), - &file.path, - media_type)?; - return Ok(PreviewWidget::MediaView(mediaview)); - } - "image" if has_media => { - // Show animation while image is loading, Drop stops it automatically - Ticker::start_ticking(core.get_sender()); - let imgview = ImgView::new_from_file(core.clone(), - &file.path())?; - return Ok(PreviewWidget::ImgView(imgview)); - } - "audio" if has_media => { - let media_type = crate::mediaview::MediaType::Audio; - let mediaview = MediaView::new_from_file(core.clone(), - &file.path, - media_type)?; - return Ok(PreviewWidget::MediaView(mediaview)); - } - "text" if mime.subtype() == "plain" => { - return Ok(Previewer::preview_text(&file, - &core, - &stale, - &animator)?); - } - _ => { - let preview = Previewer::preview_external(&file, - &core, - &stale, - &animator); - if preview.is_ok() { - return Ok(preview?); - } + if let Some(mime) = file.get_mime().log_and().ok() { + let mime_type = mime.type_().as_str(); + let is_gif = mime.subtype() == "gif"; + let has_media = core.config().media_available(); + + match mime_type { + _ if mime_type == "video" || is_gif && has_media => { + let media_type = crate::mediaview::MediaType::Video; + let mediaview = + MediaView::new_from_file(core.clone(), &file.path, media_type)?; + return Ok(PreviewWidget::MediaView(mediaview)); + } + "image" if has_media => { + let imgview = ImgView::new_from_file(core.clone(), &file.path())?; + return Ok(PreviewWidget::ImgView(imgview)); + } + "audio" if has_media => { + let media_type = crate::mediaview::MediaType::Audio; + let mediaview = + MediaView::new_from_file(core.clone(), &file.path, media_type)?; + return Ok(PreviewWidget::MediaView(mediaview)); + } + "text" if mime.subtype() == "plain" => { + return Ok(Previewer::preview_text(&file, &core, &stale, &animator)?); + } + _ => { + let preview = Previewer::preview_external(&file, &core, &stale, &animator); + if preview.is_ok() { + return Ok(preview?); } } } + } - let mut blank = TextView::new_blank(&core); - blank.set_coordinates(&coordinates).log(); - blank.refresh().log(); - blank.animate_slide_up(Some(&animator)).log(); - return Ok(PreviewWidget::TextView(blank)) - - }))) + let mut blank = TextView::new_blank(&core); + blank.set_coordinates(&coordinates).log(); + blank.refresh().log(); + blank.animate_slide_up(Some(&animator)).log(); + return Ok(PreviewWidget::TextView(blank)); + }))) } pub fn reload(&mut self) { @@ -463,20 +451,22 @@ impl Previewer { } } - fn preview_failed(file: &File) -> HResult { HError::preview_failed(file) } - fn preview_dir(file: &File, - cache: FsCache, - core: &WidgetCore, - stale: &Stale, - animator: &Stale) - -> HResult { + fn preview_dir( + file: &File, + cache: FsCache, + core: &WidgetCore, + stale: &Stale, + animator: &Stale, + ) -> HResult { use crate::dirty::Dirtyable; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } let source = FileSource::Path(file.clone()); let mut file_list = ListView::builder(core.clone(), source) @@ -484,7 +474,9 @@ impl Previewer { .with_stale(stale.clone()) .build()?; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } file_list.animate_slide_up(Some(animator))?; file_list.core.set_clean(); @@ -492,29 +484,25 @@ impl Previewer { Ok(PreviewWidget::FileList(file_list)) } - fn preview_text(file: &File, - core: &WidgetCore, - stale: &Stale, - animator: &Stale) - -> HResult { - // Show animation while text is loading - let mut ticker = Ticker::start_ticking(core.get_sender()); - + fn preview_text( + file: &File, + core: &WidgetCore, + stale: &Stale, + animator: &Stale, + ) -> HResult { let lines = core.coordinates.ysize() as usize; - - let mut textview - = TextView::new_from_file_limit_lines(&core, - &file, - lines)?; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + let mut textview = TextView::new_from_file_limit_lines(&core, &file, lines)?; + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } textview.set_coordinates(&core.coordinates)?; textview.refresh()?; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } - // Prevent flicker during slide up - ticker.stop_ticking(); textview.animate_slide_up(Some(animator))?; Ok(PreviewWidget::TextView(textview)) } @@ -543,62 +531,65 @@ impl Previewer { *pid_ = Some(pid); } - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } let output = process.wait_with_output()?; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } { let mut pid_ = SUBPROC.lock()?; *pid_ = None; } - - //let status = output.status.code()?; let output = std::str::from_utf8(&output.stdout)? .to_string() - .lines().map(|s| s.to_string()) + .lines() + .map(|s| s.to_string()) .collect(); Ok(output) } - fn preview_external(file: &File, - core: &WidgetCore, - stale: &Stale, - animator: &Stale) - -> HResult { - // Show animation while preview is being generated - let mut ticker = Ticker::start_ticking(core.get_sender()); - + fn preview_external( + file: &File, + core: &WidgetCore, + stale: &Stale, + animator: &Stale, + ) -> HResult { let previewer = if core.config().graphics.as_str() != "unicode" { - find_previewer(&file, true)? + find_previewer(&file, true)? } else { find_previewer(&file, false)? }; match previewer { ExtPreviewer::Text(previewer) => { - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } let lines = Previewer::run_external(previewer, file, stale)?; - if stale.is_stale()? { return Previewer::preview_failed(&file) } + if stale.is_stale()? { + return Previewer::preview_failed(&file); + } let mut textview = TextView::new_blank(&core); textview.set_lines(lines)?; textview.set_coordinates(&core.coordinates).log(); textview.refresh().log(); - // Prevent flicker during slide up - ticker.stop_ticking(); textview.animate_slide_up(Some(animator)).log(); Ok(PreviewWidget::TextView(textview)) - }, + } ExtPreviewer::Graphics(previewer) => { let lines = Previewer::run_external(previewer, file, stale)?; - let gfile = lines.first()?; - let imgview = ImgView::new_from_file(core.clone(), - &PathBuf::from(&gfile))?; + let gfile = lines.first().ok_or_else(|| HError::NoneError)?; + let imgview = ImgView::new_from_file(core.clone(), &PathBuf::from(&gfile))?; + Ok(PreviewWidget::ImgView(imgview)) } } @@ -616,9 +607,7 @@ impl Widget for Previewer { fn config_loaded(&mut self) -> HResult<()> { use PreviewWidget::*; - let show_hidden = self.core - .config() - .show_hidden(); + let show_hidden = self.core.config().show_hidden(); match self.widget.widget_mut() { Ok(FileList(filelist)) => { @@ -627,9 +616,8 @@ impl Widget for Previewer { if setting != show_hidden { self.reload(); } - } - Ok(_) => {}, + Ok(_) => {} Err(_) => self.reload(), } @@ -663,7 +651,7 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.get_core(), PreviewWidget::TextView(widget) => widget.get_core(), PreviewWidget::ImgView(widget) => widget.get_core(), - PreviewWidget::MediaView(widget) => widget.get_core() + PreviewWidget::MediaView(widget) => widget.get_core(), } } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { @@ -671,7 +659,7 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.get_core_mut(), PreviewWidget::TextView(widget) => widget.get_core_mut(), PreviewWidget::ImgView(widget) => widget.get_core_mut(), - PreviewWidget::MediaView(widget) => widget.get_core_mut() + PreviewWidget::MediaView(widget) => widget.get_core_mut(), } } fn set_coordinates(&mut self, coordinates: &Coordinates) -> HResult<()> { @@ -687,7 +675,7 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.refresh(), PreviewWidget::TextView(widget) => widget.refresh(), PreviewWidget::ImgView(widget) => widget.refresh(), - PreviewWidget::MediaView(widget) => widget.refresh() + PreviewWidget::MediaView(widget) => widget.refresh(), } } fn get_drawlist(&self) -> HResult { @@ -695,7 +683,7 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.get_drawlist(), PreviewWidget::TextView(widget) => widget.get_drawlist(), PreviewWidget::ImgView(widget) => widget.get_drawlist(), - PreviewWidget::MediaView(widget) => widget.get_drawlist() + PreviewWidget::MediaView(widget) => widget.get_drawlist(), } } @@ -704,7 +692,7 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.render_footer(), PreviewWidget::TextView(widget) => widget.render_footer(), PreviewWidget::ImgView(widget) => widget.render_footer(), - PreviewWidget::MediaView(widget) => widget.render_footer() + PreviewWidget::MediaView(widget) => widget.render_footer(), } } @@ -713,13 +701,15 @@ impl Widget for PreviewWidget { PreviewWidget::FileList(widget) => widget.on_key(key), PreviewWidget::TextView(widget) => widget.on_key(key), PreviewWidget::ImgView(widget) => widget.on_key(key), - PreviewWidget::MediaView(widget) => widget.on_key(key) + PreviewWidget::MediaView(widget) => widget.on_key(key), } } } - -impl Widget for Box where T: Widget + ?Sized { +impl Widget for Box +where + T: Widget + ?Sized, +{ fn get_core(&self) -> HResult<&WidgetCore> { Ok((**self).get_core()?) } diff --git a/src/proclist.rs b/src/proclist.rs index 36c724e..417f8a8 100644 --- a/src/proclist.rs +++ b/src/proclist.rs @@ -1,26 +1,26 @@ -use std::sync::{Arc, Mutex}; -use std::sync::mpsc::Sender; -use std::process::{Child, Command}; -use std::os::unix::process::{CommandExt, ExitStatusExt}; -use std::io::{BufRead, BufReader}; use std::ffi::OsString; +use std::io::{BufRead, BufReader}; use std::os::unix::ffi::OsStrExt; +use std::os::unix::process::{CommandExt, ExitStatusExt}; +use std::process::{Child, Command}; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; +use async_value::Stale; +use osstrtools::{OsStrConcat, OsStrTools, OsStringTools}; use termion::event::Key; use unicode_width::UnicodeWidthStr; -use osstrtools::{OsStringTools, OsStrTools, OsStrConcat}; -use async_value::Stale; -use crate::listview::{Listable, ListView}; -use crate::textview::TextView; -use crate::widget::{Widget, Events, WidgetCore}; use crate::coordinates::Coordinates; -use crate::preview::AsyncWidget; use crate::dirty::Dirtyable; +use crate::fail::{ErrorLog, HError, HResult}; +use crate::files::File; use crate::hbox::HBox; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::listview::{ListView, Listable}; +use crate::preview::AsyncWidget; use crate::term::{self, ScreenExt}; -use crate::files::File; +use crate::textview::TextView; +use crate::widget::{Events, Widget, WidgetCore}; #[derive(Debug)] struct Process { @@ -29,8 +29,7 @@ struct Process { output: Arc>, status: Arc>>, success: Arc>>, - sender: Sender - + sender: Sender, } pub struct Cmd { @@ -47,7 +46,9 @@ pub struct Cmd { impl Cmd { fn process(&mut self) -> Vec { // Split the string now, so inserted files aren't screwed up by substitutions - let cmd = self.cmd.split(" ") + let cmd = self + .cmd + .split(" ") .into_iter() .map(|s| s.to_os_string()) .collect(); @@ -59,13 +60,17 @@ impl Cmd { cmd } - fn perform_substitution(&self, - cmd: Vec, - pat: &str, - files: Vec) -> Vec { - if !self.cmd.contains(pat) { return cmd; } + fn perform_substitution( + &self, + cmd: Vec, + pat: &str, + files: Vec, + ) -> Vec { + if !self.cmd.contains(pat) { + return cmd; + } - let files = files + let files = files .into_iter() .map(|file| // strip out the cwd part to make path shorter @@ -80,25 +85,30 @@ impl Cmd { // If this part isn't the pattern, just return it as is match part != pat { true => part, - false => part.splice(pat, - &files) - .assemble_with_sep_and_wrap(" ", "'") + false => part + .splice(pat, &files) + .assemble_with_sep_and_wrap(" ", "'"), } }) .collect() } fn substitute_cwd_files(&mut self, cmd: Vec) -> Vec { - if self.cwd_files.is_none() { return cmd; } + if self.cwd_files.is_none() { + return cmd; + } let files = self.cwd_files.take().unwrap(); self.perform_substitution(cmd, "$s", files) } fn substitute_tab_files(&mut self, cmd: Vec) -> Vec { - if self.tab_files.is_none() { return cmd; } + if self.tab_files.is_none() { + return cmd; + } let tab_files = self.tab_files.take().unwrap(); - tab_files.into_iter() + tab_files + .into_iter() .enumerate() .fold(cmd, |cmd, (i, tab_files)| { let tab_files_pat = String::from(format!("${}s", i)); @@ -107,10 +117,13 @@ impl Cmd { } fn substitute_tab_paths(&mut self, cmd: Vec) -> Vec { - if self.tab_paths.is_none() { return cmd; } + if self.tab_paths.is_none() { + return cmd; + } let tab_paths = self.tab_paths.take().unwrap(); - tab_paths.into_iter() + tab_paths + .into_iter() .enumerate() .fold(cmd, |cmd, (i, tab_path)| { let tab_path_pat = String::from(format!("${}", i)); @@ -136,7 +149,11 @@ impl Process { let pid = self.handle.lock()?.id(); std::thread::spawn(move || -> HResult<()> { - let stdout = handle.lock()?.stdout.take()?; + let stdout = handle + .lock()? + .stdout + .take() + .ok_or_else(|| HError::NoneError)?; let mut stdout = BufReader::new(stdout); let mut processor = move |cmd, sender: &Sender| -> HResult<()> { loop { @@ -144,7 +161,9 @@ impl Process { let len = buffer.len(); let buffer = String::from_utf8_lossy(buffer); - if len == 0 { return Ok(()) } + if len == 0 { + return Ok(()); + } output.lock()?.push_str(&buffer); @@ -163,32 +182,32 @@ impl Process { let proc_success = proc_status.success(); let proc_status = match proc_status.code() { Some(status) => status, - None => proc_status.signal().unwrap_or(-1) + None => proc_status.signal().unwrap_or(-1), }; *success.lock()? = Some(proc_success); *status.lock()? = Some(proc_status); - let color_success = - if proc_success { - format!("{}successfully", term::color_green()) - } else { - format!("{}unsuccessfully", term::color_red()) - }; + let color_success = if proc_success { + format!("{}successfully", term::color_green()) + } else { + format!("{}unsuccessfully", term::color_red()) + }; - let color_status = - if proc_success { - format!("{}{}", term::color_green(), proc_status) - } else { - format!("{}{}", term::color_red(), proc_status) - }; - - let status = format!("Process: {}:{} exited {}{} with status: {}", - cmd, - pid, - color_success, - term::normal_color(), - color_status); + let color_status = if proc_success { + format!("{}{}", term::color_green(), proc_status) + } else { + format!("{}{}", term::color_red(), proc_status) + }; + + let status = format!( + "Process: {}:{} exited {}{} with status: {}", + cmd, + pid, + color_success, + term::normal_color(), + color_status + ); sender.send(Events::Status(status))?; } Ok(()) @@ -200,11 +219,14 @@ impl Process { impl Listable for ListView> { type Item = (); - fn len(&self) -> usize { self.content.len() } + fn len(&self) -> usize { + self.content.len() + } fn render(&self) -> Vec { - self.content.iter().map(|proc| { - self.render_proc(proc).unwrap() - }).collect() + self.content + .iter() + .map(|proc| self.render_proc(proc).unwrap()) + .collect() } fn on_refresh(&mut self) -> HResult<()> { self.core.set_dirty(); @@ -227,7 +249,8 @@ impl ListView> { // Nicer for display let short = "~"; - let short_cmd = cmd_args.clone() + let short_cmd = cmd_args + .clone() .replace(&home, short) .replace("'\''", "'") .replace("\"", "") @@ -256,14 +279,15 @@ impl ListView> { fn run_proc_raw(&mut self, cmd: Cmd) -> HResult<()> { let real_cmd = cmd.cmd; - let short_cmd = cmd.short_cmd - .unwrap_or(real_cmd - .to_string_lossy() - .to_string()); + let short_cmd = cmd + .short_cmd + .unwrap_or(real_cmd.to_string_lossy().to_string()); let args = cmd.args.unwrap_or(vec![]); let vars = cmd.vars.unwrap_or(vec![]); - self.core.show_status(&format!("Running: {}", &short_cmd)).log(); + self.core + .show_status(&format!("Running: {}", &short_cmd)) + .log(); // Need pre_exec here to interleave stderr with stdout let handle = unsafe { @@ -273,15 +297,17 @@ impl ListView> { .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::piped()) // Without this stderr would be separate which is no good for procview - .pre_exec(|| { libc::dup2(1, 2); Ok(()) }) + .pre_exec(|| { + libc::dup2(1, 2); + Ok(()) + }) .spawn() }; let handle = match handle { Ok(handle) => handle, Err(e) => { - let msg = format!("Error! Failed to start process: {}", - e); + let msg = format!("Error! Failed to start process: {}", e); self.core.show_status(&msg)?; return Err(e)?; } @@ -293,7 +319,7 @@ impl ListView> { output: Arc::new(Mutex::new(String::new())), status: Arc::new(Mutex::new(None)), success: Arc::new(Mutex::new(None)), - sender: self.get_core()?.get_sender() + sender: self.get_core()?.get_sender(), }; proc.read_proc()?; self.content.push(proc); @@ -302,67 +328,63 @@ impl ListView> { fn run_proc_raw_fg(&mut self, cmd: Cmd) -> HResult<()> { let real_cmd = cmd.cmd; - let short_cmd = cmd.short_cmd - .unwrap_or(real_cmd - .to_string_lossy() - .to_string()); + let short_cmd = cmd + .short_cmd + .unwrap_or(real_cmd.to_string_lossy().to_string()); let args = cmd.args.unwrap_or(vec![]); - self.core.show_status(&format!("Running (fg): {}", &short_cmd)).log(); + self.core + .show_status(&format!("Running (fg): {}", &short_cmd)) + .log(); - self.core.screen.goto_xy(0,0)?; + self.core.screen.goto_xy(0, 0)?; self.core.screen.reset()?; self.core.screen.suspend()?; - match Command::new(real_cmd) - .args(args) - .status() { - Ok(status) => { - let color_success = - if status.success() { - format!("{}successfully", term::color_green()) - } else { - format!("{}unsuccessfully", term::color_red()) - }; - - let color_status = - if status.success() { - format!("{}{}", - term::color_green(), - status.code().unwrap_or(status - .signal() - .unwrap_or(-1))) - } else { - format!("{}{}", - term::color_red(), - status.code().unwrap_or(status - .signal() - .unwrap_or(-1))) - - }; - - - let procinfo = format!("{} exited {}{}{} with status: {}", - short_cmd, - color_success, - term::reset(), - term::status_bg(), - color_status); - - self.core.show_status(&procinfo)?; - }, - err @ Err(_) => { - self.core.show_status(&format!("{}{} ", - "Couldn't start process:", - short_cmd))?; - err?; - } + match Command::new(real_cmd).args(args).status() { + Ok(status) => { + let color_success = if status.success() { + format!("{}successfully", term::color_green()) + } else { + format!("{}unsuccessfully", term::color_red()) + }; + + let color_status = if status.success() { + format!( + "{}{}", + term::color_green(), + status.code().unwrap_or(status.signal().unwrap_or(-1)) + ) + } else { + format!( + "{}{}", + term::color_red(), + status.code().unwrap_or(status.signal().unwrap_or(-1)) + ) + }; + + let procinfo = format!( + "{} exited {}{}{} with status: {}", + short_cmd, + color_success, + term::reset(), + term::status_bg(), + color_status + ); + + self.core.show_status(&procinfo)?; + } + err @ Err(_) => { + self.core + .show_status(&format!("{}{} ", "Couldn't start process:", short_cmd))?; + err?; } + } Ok(()) } fn kill_proc(&mut self) -> HResult<()> { - let proc = self.selected_proc()?; + let proc = self.selected_proc().ok_or_else(|| HError::NoneError)?; proc.handle.lock()?.kill()?; Ok(()) } @@ -393,22 +415,25 @@ impl ListView> { let padding = xsize - padding as u16; let color_status = match *proc.success.lock().unwrap() { - Some(false) => { format!("{}{}", term::color_red(), status) } - _ => { status } + Some(false) => format!("{}{}", term::color_red(), status), + _ => status, }; Ok(format!( "{}{}{}{}{}{}", termion::cursor::Save, - format!("{}{:padding$}{}", - term::normal_color(), - &sized_string, - term::normal_color(), - padding = padding as usize), + format!( + "{}{:padding$}{}", + term::normal_color(), + &sized_string, + term::normal_color(), + padding = padding as usize + ), termion::cursor::Restore, termion::cursor::Right(status_pos), term::highlight_color(), - color_status)) + color_status + )) } } @@ -422,25 +447,25 @@ impl Widget for ProcViewWidgets { fn get_core(&self) -> HResult<&WidgetCore> { match self { ProcViewWidgets::List(widget) => widget.get_core(), - ProcViewWidgets::TextView(widget) => widget.get_core() + ProcViewWidgets::TextView(widget) => widget.get_core(), } } fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { match self { ProcViewWidgets::List(widget) => widget.get_core_mut(), - ProcViewWidgets::TextView(widget) => widget.get_core_mut() + ProcViewWidgets::TextView(widget) => widget.get_core_mut(), } } fn refresh(&mut self) -> HResult<()> { match self { ProcViewWidgets::List(widget) => widget.refresh(), - ProcViewWidgets::TextView(widget) => widget.refresh() + ProcViewWidgets::TextView(widget) => widget.refresh(), } } fn get_drawlist(&self) -> HResult { match self { ProcViewWidgets::List(widget) => widget.get_drawlist(), - ProcViewWidgets::TextView(widget) => widget.get_drawlist() + ProcViewWidgets::TextView(widget) => widget.get_drawlist(), } } } @@ -450,26 +475,26 @@ pub struct ProcView { core: WidgetCore, hbox: HBox, viewing: Option, - animator: Stale + animator: Stale, } impl HBox { fn get_listview(&self) -> &ListView> { match &self.widgets[0] { ProcViewWidgets::List(listview) => listview, - _ => unreachable!() + _ => unreachable!(), } } fn get_listview_mut(&mut self) -> &mut ListView> { match &mut self.widgets[0] { ProcViewWidgets::List(listview) => listview, - _ => unreachable!() + _ => unreachable!(), } } fn get_textview(&mut self) -> &mut AsyncWidget { match &mut self.widgets[1] { ProcViewWidgets::TextView(textview) => textview, - _ => unreachable!() + _ => unreachable!(), } } } @@ -491,11 +516,11 @@ impl ProcView { core: core.clone(), hbox: hbox, viewing: None, - animator: Stale::new() + animator: Stale::new(), } } - fn get_listview(& self) -> & ListView> { + fn get_listview(&self) -> &ListView> { self.hbox.get_listview() } @@ -518,7 +543,9 @@ impl ProcView { } pub fn remove_proc(&mut self) -> HResult<()> { - if self.get_listview_mut().content.len() == 0 { return Ok(()) } + if self.get_listview_mut().content.len() == 0 { + return Ok(()); + } self.get_listview_mut().remove_proc()?; self.get_textview().get_core()?.clear().log(); self.get_textview().widget_mut()?.set_text("").log(); @@ -530,17 +557,25 @@ impl ProcView { if Some(self.get_listview_mut().get_selection()) == self.viewing { return Ok(()); } - let output = self.get_listview_mut().selected_proc()?.output.lock()?.clone(); + let output = self + .get_listview_mut() + .selected_proc() + .ok_or_else(|| HError::NoneError)? + .output + .lock()? + .clone(); let animator = self.animator.clone(); animator.set_fresh().log(); - self.get_textview().change_to(move |_, core| { - let mut textview = TextView::new_blank(&core); - textview.set_text(&output).log(); - textview.animate_slide_up(Some(&animator)).log(); - Ok(textview) - }).log(); + self.get_textview() + .change_to(move |_, core| { + let mut textview = TextView::new_blank(&core); + textview.set_text(&output).log(); + textview.animate_slide_up(Some(&animator)).log(); + Ok(textview) + }) + .log(); self.viewing = Some(self.get_listview_mut().get_selection()); Ok(()) @@ -604,9 +639,7 @@ impl Widget for ProcView { .filter(|proc| proc.status.lock().unwrap().is_none()) .count(); - let header = format!("Running processes: {} / {}", - procs_running, - procs_num); + let header = format!("Running processes: {} / {}", procs_running, procs_num); Ok(header) } @@ -622,36 +655,42 @@ impl Widget for ProcView { let proc_success = proc.success.lock()?; let procinfo = if proc_status.is_some() { - let color_success = - if let Some(_) = *proc_success { - format!("{}successfully", term::color_green()) + let color_success = if let Some(_) = *proc_success { + format!("{}successfully", term::color_green()) + } else { + format!("{}unsuccessfully", term::color_red()) + }; + + let color_status = if let Some(success) = *proc_success { + if success { + format!("{}{}", term::color_green(), proc_status.unwrap()) } else { - format!("{}unsuccessfully", term::color_red()) - }; - - let color_status = - if let Some(success) = *proc_success { - if success { - format!("{}{}", term::color_green(), proc_status.unwrap()) - } else { - format!("{}{}", term::color_red(), proc_status.unwrap()) - } - } else { "wtf".to_string() }; - - let procinfo = format!("{}:{} exited {}{}{} with status: {}", - cmd, - pid, - color_success, - term::reset(), - term::status_bg(), - color_status); + format!("{}{}", term::color_red(), proc_status.unwrap()) + } + } else { + "wtf".to_string() + }; + + let procinfo = format!( + "{}:{} exited {}{}{} with status: {}", + cmd, + pid, + color_success, + term::reset(), + term::status_bg(), + color_status + ); procinfo - } else { "still running".to_string() }; + } else { + "still running".to_string() + }; let footer = term::sized_string_u(&procinfo, xsize); Ok(footer) - } else { Ok("No proccesses".to_string()) } + } else { + Ok("No proccesses".to_string()) + } } fn refresh(&mut self) -> HResult<()> { @@ -677,8 +716,6 @@ impl Widget for ProcView { } } - - use crate::keybind::*; impl Acting for ProcView { @@ -696,9 +733,11 @@ impl Acting for ProcView { use ProcessAction::*; match action { - Close => { self.animator.set_stale().log(); - self.core.clear().log(); - Err(HError::PopupFinnished)? } + Close => { + self.animator.set_stale().log(); + self.core.clear().log(); + Err(HError::PopupFinnished)? + } Remove => self.remove_proc()?, Kill => self.get_listview_mut().kill_proc()?, FollowOutput => self.toggle_follow()?, @@ -707,16 +746,15 @@ impl Acting for ProcView { ScrollOutputPageDown => self.page_down()?, ScrollOutputPageUp => self.page_up()?, ScrollOutputBottom => self.scroll_bottom()?, - ScrollOutputTop => self.scroll_top()? + ScrollOutputTop => self.scroll_top()?, } Ok(()) } } - impl Acting for ListView> { - type Action=ProcessAction; + type Action = ProcessAction; fn search_in(&self) -> Bindings { self.core.config().keybinds.process @@ -726,8 +764,18 @@ impl Acting for ListView> { use Movement::*; match movement { - Up(n) => { for _ in 0..*n { self.move_up(); }; self.refresh()?; } - Down(n) => { for _ in 0..*n { self.move_down(); }; self.refresh()?; } + Up(n) => { + for _ in 0..*n { + self.move_up(); + } + self.refresh()?; + } + Down(n) => { + for _ in 0..*n { + self.move_down(); + } + self.refresh()?; + } PageUp => self.page_up(), PageDown => self.page_down(), Top => self.move_top(), diff --git a/src/quick_actions.rs b/src/quick_actions.rs index 74ff9db..ded0e9d 100644 --- a/src/quick_actions.rs +++ b/src/quick_actions.rs @@ -3,26 +3,21 @@ use termion::event::Key; use async_value::Async; -use std::path::PathBuf; -use std::sync::{ - Arc, Mutex, - mpsc::Sender, -}; use std::ffi::OsString; +use std::path::PathBuf; use std::str::FromStr; +use std::sync::{mpsc::Sender, Arc, Mutex}; - -use crate::fail::{HResult, HError, KeyBindError, ErrorLog}; -use crate::widget::{Widget, WidgetCore, Events}; -use crate::foldview::{Foldable, FoldableWidgetExt, ActingExt}; -use crate::listview::ListView; -use crate::proclist::ProcView; +use crate::fail::{ErrorLog, HError, HResult, KeyBindError}; use crate::files::File; +use crate::foldview::{ActingExt, Foldable, FoldableWidgetExt}; +use crate::keybind::{Bindings, Movement, QuickActionAction}; +use crate::listview::ListView; use crate::paths; +use crate::proclist::ProcView; use crate::term; use crate::term::ScreenExt; -use crate::keybind::{Bindings, Movement, QuickActionAction}; - +use crate::widget::{Events, Widget, WidgetCore}; pub type QuickActionView = ListView>; @@ -30,44 +25,47 @@ impl FoldableWidgetExt for ListView> { fn on_refresh(&mut self) -> HResult<()> { for action in self.content.iter_mut() { action.actions.pull_async().ok(); - let content = action.actions - .get() - .map(|actions| { - actions - .iter() - .map(|action| { - let queries = action.queries - .iter() - .map(|q| String::from(":") + &q.to_string() + "?") - .collect::(); - format!("{}{}", - crate::term::highlight_color(), - action.title.clone() + &queries + "\n") - }) - .collect::() - }); + let content = action.actions.get().map(|actions| { + actions + .iter() + .map(|action| { + let queries = action + .queries + .iter() + .map(|q| String::from(":") + &q.to_string() + "?") + .collect::(); + format!( + "{}{}", + crate::term::highlight_color(), + action.title.clone() + &queries + "\n" + ) + }) + .collect::() + }); if let Ok(content) = content { - let content = format!("{}{}\n{}", - crate::term::status_bg(), - action.description, content); + let content = format!( + "{}{}\n{}", + crate::term::status_bg(), + action.description, + content + ); let lines = content.lines().count(); action.content = Some(content); action.lines = lines; } } - Ok(()) } fn render_header(&self) -> HResult { - let mime = &self.content.get(0)?.mime; + let mime = &self.content.get(0).ok_or_else(|| HError::NoneError)?.mime; Ok(format!("QuickActions for MIME: {}", mime)) } fn on_key(&mut self, key: Key) -> HResult<()> { - ActingExt::do_key_ext(self,key) + ActingExt::do_key_ext(self, key) } fn render(&self) -> Vec { @@ -75,14 +73,15 @@ impl FoldableWidgetExt for ListView> { self.content .iter() .fold(Vec::::new(), |mut acc, atype| { - let mut alist = atype.render() + let mut alist = atype + .render() .iter() .enumerate() .map(|(i, line)| { - term::sized_string_u(&format!("[{}]: {}", - self.num_to_letter(acc.len() + i), - line), - xsize) + term::sized_string_u( + &format!("[{}]: {}", self.num_to_letter(acc.len() + i), line), + xsize, + ) }) .collect::>(); @@ -102,7 +101,7 @@ impl ActingExt for QuickActionView { fn movement(&mut self, movement: &Movement) -> HResult<()> { match movement { Movement::Right => self.run_action(None), - _ => Err(KeyBindError::MovementUndefined)? + _ => Err(KeyBindError::MovementUndefined)?, } } @@ -135,8 +134,6 @@ impl ActingExt for QuickActionView { } } - - impl ListView> { fn render(&self) -> Vec { vec![] @@ -155,7 +152,7 @@ impl ListView> { fn run_action(&mut self, num: Option) -> HResult<()> { num.map(|num| self.set_selection(num)); - let current_fold = self.current_fold()?; + let current_fold = self.current_fold().ok_or_else(|| HError::NoneError)?; let fold_start_pos = self.fold_start_pos(current_fold); let selection = self.get_selection(); let selected_action_index = selection - fold_start_pos; @@ -170,9 +167,11 @@ impl ListView> { .actions // -1 because fold description takes one slot .get()?[selected_action_index - 1] - .run(self.content[0].files.clone(), - &self.core, - self.content[0].proc_view.clone())?; + .run( + self.content[0].files.clone(), + &self.core, + self.content[0].proc_view.clone(), + )?; self.core.screen()?.clear()?; Ok(()) @@ -181,26 +180,20 @@ impl ListView> { fn num_to_letter(&self, num: usize) -> String { if num > 9 && num < (CHARS.chars().count() + 10) { // subtract number keys - CHARS.chars() - .skip(num-10) - .take(1) - .collect() - } else if num < 10{ + CHARS.chars().skip(num - 10).take(1).collect() + } else if num < 10 { format!("{}", num) } else { String::from("..") } - } fn letter_to_num(&self, letter: char) -> Option { - CHARS.chars() + CHARS + .chars() .position(|ch| ch == letter) .map(|pos| pos + 10) - .or_else(|| - format!("{}", letter) - .parse::() - .ok()) + .or_else(|| format!("{}", letter).parse::().ok()) } } @@ -208,22 +201,23 @@ impl ListView> { static CHARS: &str = "bcdefgimoqrstuvxyz"; impl QuickActions { - pub fn new(files: Vec, - mime: mime::Mime, - subpath: &str, - description: String, - sender: Sender, - proc_view: Arc>) -> HResult { + pub fn new( + files: Vec, + mime: mime::Mime, + subpath: &str, + description: String, + sender: Sender, + proc_view: Arc>, + ) -> HResult { let mut actions = files.get_actions(mime.clone(), subpath.to_string()); - actions.on_ready(move |_,_| { + actions.on_ready(move |_, _| { sender.send(Events::WidgetReady).ok(); Ok(()) })?; actions.run()?; - Ok(QuickActions { description: description, files: files, @@ -232,47 +226,53 @@ impl QuickActions { lines: 1, folded: false, actions: actions, - proc_view: proc_view + proc_view: proc_view, }) } } -pub fn open(files: Vec, - sender: Sender, - core: WidgetCore, - proc_view: Arc>) -> HResult<()> { - let mime = files.common_mime() +pub fn open( + files: Vec, + sender: Sender, + core: WidgetCore, + proc_view: Arc>, +) -> HResult<()> { + let mime = files + .common_mime() .unwrap_or_else(|| Mime::from_str("*/").unwrap()); - - let act = QuickActions::new(files.clone(), - mime.clone(), - "", - String::from("UniActions"), - sender.clone(), - proc_view.clone()).unwrap(); + let act = QuickActions::new( + files.clone(), + mime.clone(), + "", + String::from("UniActions"), + sender.clone(), + proc_view.clone(), + ) + .unwrap(); let mut action_view: QuickActionView = ListView::new(&core, vec![]); action_view.content = vec![act]; - let subdir = mime.type_().as_str(); - let act_base = QuickActions::new(files.clone(), - mime.clone(), - subdir, - String::from("BaseActions"), - sender.clone(), - proc_view.clone()); - - let subdir = &format!("{}/{}", - mime.type_().as_str(), - mime.subtype().as_str()); - let act_sub = QuickActions::new(files, - mime.clone(), - subdir, - String::from("SubActions"), - sender, - proc_view); + let act_base = QuickActions::new( + files.clone(), + mime.clone(), + subdir, + String::from("BaseActions"), + sender.clone(), + proc_view.clone(), + ); + + let subdir = &format!("{}/{}", mime.type_().as_str(), mime.subtype().as_str()); + let act_sub = QuickActions::new( + files, + mime.clone(), + subdir, + String::from("SubActions"), + sender, + proc_view, + ); act_base.map(|act| action_view.content.push(act)).ok(); act_sub.map(|act| action_view.content.push(act)).ok(); @@ -283,12 +283,11 @@ pub fn open(files: Vec, Err(HError::RefreshParent) => continue, Err(HError::WidgetResizedError) => continue, Err(HError::TerminalResizedError) => continue, - r @ _ => break r + r @ _ => break r, } } } - #[derive(Debug)] pub struct QuickActions { description: String, @@ -298,7 +297,7 @@ pub struct QuickActions { lines: usize, folded: bool, actions: Async>, - proc_view: Arc> + proc_view: Arc>, } impl Foldable for QuickActions { @@ -307,9 +306,7 @@ impl Foldable for QuickActions { } fn render_description(&self) -> String { - format!("{}{}", - term::status_bg(), - &self.description) + format!("{}{}", term::status_bg(), &self.description) } fn content(&self) -> Option<&String> { @@ -317,9 +314,11 @@ impl Foldable for QuickActions { } fn lines(&self) -> usize { - if self.folded - { 1 } else - { self.lines } + if self.folded { + 1 + } else { + self.lines + } } fn toggle_fold(&mut self) { @@ -331,17 +330,13 @@ impl Foldable for QuickActions { } } - - - - #[derive(Debug)] pub struct QuickAction { path: PathBuf, title: String, queries: Vec, sync: bool, - mime: mime::Mime + mime: mime::Mime, } impl QuickAction { @@ -355,49 +350,48 @@ impl QuickAction { title, queries, sync, - mime + mime, } } - fn run(&self, - files: Vec, - core: &WidgetCore, - proc_view: Arc>) -> HResult<()> { + fn run( + &self, + files: Vec, + core: &WidgetCore, + proc_view: Arc>, + ) -> HResult<()> { use crate::minibuffer::MiniBufferEvent::*;; - let answers = self.queries - .iter() - .fold(Ok(vec![]), |mut acc, query| { - // If error occured/input was cancelled just skip querying - // Turn into try_fold? - if acc.is_err() { return acc; } - - match core.minibuffer(query) { - Err(HError::MiniBufferEvent(Empty)) => { - acc.as_mut() - .map(|acc| acc.push((OsString::from(query), - OsString::from("")))) - .ok(); - acc - } - Ok(input) => { - acc.as_mut() - .map(|acc| acc.push((OsString::from(query), - OsString::from(input)))) - .ok(); - acc - } - Err(err) => Err(err) - } - })?; - - let cwd = files.get(0)?.parent_as_file()?; + let answers = self.queries.iter().fold(Ok(vec![]), |mut acc, query| { + // If error occured/input was cancelled just skip querying + // Turn into try_fold? + if acc.is_err() { + return acc; + } - let files: Vec = files.iter() - .map(|f| OsString::from(&f.path)) - .collect(); + match core.minibuffer(query) { + Err(HError::MiniBufferEvent(Empty)) => { + acc.as_mut() + .map(|acc| acc.push((OsString::from(query), OsString::from("")))) + .ok(); + acc + } + Ok(input) => { + acc.as_mut() + .map(|acc| acc.push((OsString::from(query), OsString::from(input)))) + .ok(); + acc + } + Err(err) => Err(err), + } + })?; + let cwd = files + .get(0) + .ok_or_else(|| HError::NoneError)? + .parent_as_file()?; + let files: Vec = files.iter().map(|f| OsString::from(&f.path)).collect(); if self.sync { std::process::Command::new(&self.path) @@ -415,22 +409,18 @@ impl QuickAction { cwd: cwd, cwd_files: None, tab_files: None, - tab_paths: None + tab_paths: None, }; proc_view .lock() - .map(|mut proc_view| { - proc_view.run_proc_raw(cmd) - })??; + .map(|mut proc_view| proc_view.run_proc_raw(cmd))??; Ok(()) } } } - - pub trait QuickFiles { fn common_mime(&self) -> Option; fn get_actions(&self, mime: mime::Mime, subpath: String) -> Async>; @@ -439,56 +429,43 @@ pub trait QuickFiles { impl QuickFiles for Vec { // Compute the most specific MIME shared by all files fn common_mime(&self) -> Option { - let first_mime = self - .get(0)? - .get_mime() - .log_and() - .ok(); - - - self.iter() - .fold(first_mime, |common_mime, file| { - let cur_mime = file.get_mime() - .log_and() - .ok(); - - if &cur_mime == &common_mime { - cur_mime - } else { - - // MIMEs differ, find common base - - match (cur_mime, common_mime) { - (Some(cur_mime), Some(common_mime)) => { - // Differ in suffix? - - if cur_mime.type_() == common_mime.type_() - && cur_mime.subtype() == common_mime.subtype() - { - Mime::from_str(&format!("{}/{}", - cur_mime.type_().as_str(), - cur_mime.subtype().as_str())) - .ok() - } - - // Differ in subtype? - - else if cur_mime.type_() == common_mime.type_() { - Mime::from_str(&format!("{}/", - cur_mime.type_() - .as_str())) - .ok() - - // Completely different MIME types - - } else { - None - } + let first_mime = self.get(0)?.get_mime().log_and().ok(); + + self.iter().fold(first_mime, |common_mime, file| { + let cur_mime = file.get_mime().log_and().ok(); + + if &cur_mime == &common_mime { + cur_mime + } else { + // MIMEs differ, find common base + + match (cur_mime, common_mime) { + (Some(cur_mime), Some(common_mime)) => { + // Differ in suffix? + + if cur_mime.type_() == common_mime.type_() + && cur_mime.subtype() == common_mime.subtype() + { + Mime::from_str(&format!( + "{}/{}", + cur_mime.type_().as_str(), + cur_mime.subtype().as_str() + )) + .ok() + } + // Differ in subtype? + else if cur_mime.type_() == common_mime.type_() { + Mime::from_str(&format!("{}/", cur_mime.type_().as_str())).ok() + + // Completely different MIME types + } else { + None } - _ => None - } + } + _ => None, } - }) + } + }) } fn get_actions(&self, mime: mime::Mime, subpath: String) -> Async> { @@ -496,19 +473,19 @@ impl QuickFiles for Vec { let mut apath = paths::actions_path()?; apath.push(subpath); Ok(std::fs::read_dir(apath)? - .filter_map(|file| { - let path = file.ok()?.path(); - if !path.is_dir() { - Some(QuickAction::new(path, mime.clone())) - } else { - None - } - }).collect()) + .filter_map(|file| { + let path = file.ok()?.path(); + if !path.is_dir() { + Some(QuickAction::new(path, mime.clone())) + } else { + None + } + }) + .collect()) }) } } - pub trait QuickPath { fn get_title(&self) -> String; fn get_queries(&self) -> Vec; @@ -518,33 +495,28 @@ pub trait QuickPath { impl QuickPath for PathBuf { fn get_title(&self) -> String { self.file_stem() - .map(|stem| stem - .to_string_lossy() - .splitn(2, "?") - .collect::>()[0] - .to_string()) + .map(|stem| stem.to_string_lossy().splitn(2, "?").collect::>()[0].to_string()) .unwrap_or_else(|| String::from("Filename missing!")) } fn get_queries(&self) -> Vec { self.file_stem() - .map(|stem| stem - .to_string_lossy() - .split("?") - .collect::>() - .iter() - .skip(1) - // Remove ! in queries from sync actions - .map(|q| q.trim_end_matches("!").to_string()) - .collect()) + .map(|stem| { + stem.to_string_lossy() + .split("?") + .collect::>() + .iter() + .skip(1) + // Remove ! in queries from sync actions + .map(|q| q.trim_end_matches("!").to_string()) + .collect() + }) .unwrap_or_else(|| vec![]) } fn get_sync(&self) -> bool { self.file_stem() - .map(|stem| stem - .to_string_lossy() - .ends_with("!")) + .map(|stem| stem.to_string_lossy().ends_with("!")) .unwrap_or(false) } } diff --git a/src/stats.rs b/src/stats.rs index 4fc3539..6909581 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -1,19 +1,21 @@ -use systemstat::{System, Platform}; use systemstat::data::Filesystem; +use systemstat::{Platform, System}; -use std::path::{Path, PathBuf, Component}; use std::collections::HashMap; +use std::path::{Component, Path, PathBuf}; -use crate::fail::{HResult, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult}; -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub struct FsStat { - pub stats: HashMap + pub stats: HashMap, } impl FsStat { pub fn new() -> HResult { - let mut stats = FsStat { stats: HashMap::new() }; + let mut stats = FsStat { + stats: HashMap::new(), + }; stats.refresh().log(); Ok(stats) @@ -23,7 +25,8 @@ impl FsStat { let sys = System::new(); let mounts = sys.mounts()?; - let stats = mounts.into_iter() + let stats = mounts + .into_iter() .fold(HashMap::new(), |mut stats, mount: Filesystem| { let path = PathBuf::from(&mount.fs_mounted_on); stats.insert(path, mount); @@ -42,17 +45,19 @@ impl FsStat { .filter(|mount_point| path.starts_with(&mount_point)) .collect::>(); - let deepest_match = candidates.iter() - .fold(PathBuf::new(), |mut deepest, path| { - let curren_path_len = deepest.components().count(); - let candidate_path_len = path.components().count(); + let deepest_match = candidates.iter().fold(PathBuf::new(), |mut deepest, path| { + let curren_path_len = deepest.components().count(); + let candidate_path_len = path.components().count(); - if candidate_path_len > curren_path_len { - deepest = path.to_path_buf(); - } - deepest - }); - let fs = self.stats.get(&deepest_match)?; + if candidate_path_len > curren_path_len { + deepest = path.to_path_buf(); + } + deepest + }); + let fs = self + .stats + .get(&deepest_match) + .ok_or_else(|| HError::NoneError)?; Ok(fs) } } @@ -70,7 +75,7 @@ impl FsExt for Filesystem { let dev = match dev { Component::Normal(dev) => dev.to_string_lossy().to_string() + ": ", // zfs on FBSD doesn't return a device path - _ => "".to_string() + _ => "".to_string(), }; Some(dev) } @@ -82,6 +87,4 @@ impl FsExt for Filesystem { fn get_free(&self) -> String { self.avail.to_string_as(false) } - - } diff --git a/src/tabview.rs b/src/tabview.rs index 388740f..dc28739 100644 --- a/src/tabview.rs +++ b/src/tabview.rs @@ -1,8 +1,8 @@ use termion::event::Key; -use crate::widget::{Widget, WidgetCore}; -use crate::fail::{HResult, HError, ErrorLog}; use crate::coordinates::Coordinates; +use crate::fail::{ErrorLog, HError, HResult}; +use crate::widget::{Widget, WidgetCore}; pub trait Tabbable { type Tab: Widget; @@ -21,26 +21,38 @@ pub trait Tabbable { fn on_key(&mut self, key: Key) -> HResult<()> { self.on_key_sub(key) } - fn on_refresh(&mut self) -> HResult<()> { Ok(()) } - fn on_config_loaded(&mut self) -> HResult<()> { Ok(()) } - fn on_new(&mut self) -> HResult<()> { Ok(()) } - + fn on_refresh(&mut self) -> HResult<()> { + Ok(()) + } + fn on_config_loaded(&mut self) -> HResult<()> { + Ok(()) + } + fn on_new(&mut self) -> HResult<()> { + Ok(()) + } } - #[derive(PartialEq)] -pub struct TabView where T: Widget, TabView: Tabbable { +pub struct TabView +where + T: Widget, + TabView: Tabbable, +{ pub widgets: Vec, pub active: usize, - pub core: WidgetCore + pub core: WidgetCore, } -impl TabView where T: Widget, TabView: Tabbable { +impl TabView +where + T: Widget, + TabView: Tabbable, +{ pub fn new(core: &WidgetCore) -> TabView { let mut tabview = TabView { widgets: vec![], active: 0, - core: core.clone() + core: core.clone(), }; Tabbable::on_new(&mut tabview).log(); @@ -54,7 +66,7 @@ impl TabView where T: Widget, TabView: Tabbable { } pub fn pop_widget(&mut self) -> HResult { - let widget = self.widgets.pop()?; + let widget = self.widgets.pop().ok_or_else(|| HError::NoneError)?; if self.widgets.len() <= self.active { self.active -= 1; } @@ -65,7 +77,7 @@ impl TabView where T: Widget, TabView: Tabbable { let len = self.widgets.len(); if len > 1 { self.widgets.remove(index); - if index+1 == len { + if index + 1 == len { self.active -= 1; } } @@ -112,7 +124,11 @@ impl TabView where T: Widget, TabView: Tabbable { } } -impl Widget for TabView where T: Widget, TabView: Tabbable { +impl Widget for TabView +where + T: Widget, + TabView: Tabbable, +{ fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } @@ -137,34 +153,36 @@ impl Widget for TabView where T: Widget, TabView: Tabbable { let header = self.active_tab_().render_header()?; let tab_names = self.get_tab_names(); let mut nums_length = 0; - let tabnums = (0..self.widgets.len()).map(|num| { - nums_length += format!("{}:{} ", - num, - tab_names[num].as_ref().unwrap()).len(); - if num == self.active { - format!(" {}{}:{}{}{}", + let tabnums = (0..self.widgets.len()) + .map(|num| { + nums_length += format!("{}:{} ", num, tab_names[num].as_ref().unwrap()).len(); + if num == self.active { + format!( + " {}{}:{}{}{}", crate::term::invert(), num, tab_names[num].as_ref().unwrap(), crate::term::reset(), - crate::term::header_color()) - } else { - format!(" {}:{}", num, tab_names[num].as_ref().unwrap()) - } - }).collect::(); - + crate::term::header_color() + ) + } else { + format!(" {}:{}", num, tab_names[num].as_ref().unwrap()) + } + }) + .collect::(); let nums_pos = xsize.saturating_sub(nums_length as u16); - Ok(format!("{}{}{}{}", - header, - crate::term::header_color(), - crate::term::goto_xy(nums_pos, 1), - tabnums)) + Ok(format!( + "{}{}{}{}", + header, + crate::term::header_color(), + crate::term::goto_xy(nums_pos, 1), + tabnums + )) } - fn render_footer(&self) -> HResult - { + fn render_footer(&self) -> HResult { self.active_tab_().render_footer() } @@ -179,8 +197,8 @@ impl Widget for TabView where T: Widget, TabView: Tabbable { fn on_key(&mut self, key: Key) -> HResult<()> { match self.do_key(key) { - Err(HError::WidgetUndefinedKeyError{..}) => Tabbable::on_key(self, key)?, - e @ _ => e? + Err(HError::WidgetUndefinedKeyError { .. }) => Tabbable::on_key(self, key)?, + e @ _ => e?, } Ok(()) @@ -189,7 +207,10 @@ impl Widget for TabView where T: Widget, TabView: Tabbable { use crate::keybind::*; -impl Acting for TabView where TabView: Tabbable { +impl Acting for TabView +where + TabView: Tabbable, +{ type Action = TabAction; fn search_in(&self) -> Bindings { diff --git a/src/term.rs b/src/term.rs index 1dd4a7b..1bd789d 100644 --- a/src/term.rs +++ b/src/term.rs @@ -1,14 +1,14 @@ -use std::io::{Stdout, Write, BufWriter, BufRead}; +use std::io::{BufRead, BufWriter, Stdout, Write}; use std::sync::{Arc, Mutex, RwLock}; use termion; -use termion::screen::AlternateScreen; use termion::raw::{IntoRawMode, RawTerminal}; +use termion::screen::AlternateScreen; +use crate::unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use parse_ansi::parse_bytes; -use crate::unicode_width::{UnicodeWidthStr, UnicodeWidthChar}; -use crate::fail::{HResult, ErrorLog}; +use crate::fail::{ErrorLog, HError, HResult}; use crate::trait_ext::ExtractResult; pub type TermMode = AlternateScreen>>; @@ -17,7 +17,7 @@ pub type TermMode = AlternateScreen>>; pub struct Screen { screen: Arc>, size: Arc>>, - terminal: String + terminal: String, } impl Screen { @@ -30,7 +30,7 @@ impl Screen { Ok(Screen { screen: Arc::new(Mutex::new(screen)), size: Arc::new(RwLock::new(None)), - terminal: terminal + terminal: terminal, }) } @@ -46,19 +46,20 @@ impl Screen { pub fn get_size(&self) -> HResult<(usize, usize)> { match self.size.read()?.clone() { Some((xsize, ysize)) => Ok((xsize, ysize)), - None => Ok((self.xsize()?, self.ysize()?)) + None => Ok((self.xsize()?, self.ysize()?)), } } pub fn take_size(&self) -> HResult<(usize, usize)> { - Ok(self.size.write()?.take()?) + Ok(self.size.write()?.take().ok_or_else(|| HError::NoneError)?) } pub fn set_title(&mut self, title: &str) -> HResult<()> { - if self.terminal.starts_with("xterm") || - self.terminal.starts_with("screen") || - self.terminal.starts_with("tmux"){ - write!(self, "\x1b]2;hunter: {}\x1b\\", title)?; + if self.terminal.starts_with("xterm") + || self.terminal.starts_with("screen") + || self.terminal.starts_with("tmux") + { + write!(self, "\x1b]2;hunter: {}\x1b\\", title)?; } if self.terminal.starts_with("tmux") { write!(self, "\x1bkhunter: {}\x1b\\", title)?; @@ -71,15 +72,13 @@ impl Write for Screen { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.screen .lock() - .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, - "Screen Mutex poisoned!")) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Screen Mutex poisoned!")) .and_then(|mut s| s.write(buf)) } fn flush(&mut self) -> std::io::Result<()> { self.screen .lock() - .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, - "Screen Mutex poisoned!")) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Screen Mutex poisoned!")) .and_then(|mut s| s.flush()) } } @@ -112,9 +111,7 @@ pub trait ScreenExt: Write { Ok(()) } fn clear(&mut self) -> HResult<()> { - write!(self, "{}{}", - termion::style::Reset, - termion::clear::All)?; + write!(self, "{}{}", termion::style::Reset, termion::clear::All)?; Ok(()) } fn write_str(&mut self, str: &str) -> HResult<()> { @@ -129,7 +126,7 @@ pub trait ScreenExt: Write { } fn size(&self) -> HResult<(usize, usize)> { let (xsize, ysize) = termion::terminal_size()?; - Ok(((xsize-1) as usize, (ysize-1) as usize)) + Ok(((xsize - 1) as usize, (ysize - 1) as usize)) } fn xsize(&self) -> HResult { @@ -154,15 +151,11 @@ pub trait ScreenExt: Write { impl ScreenExt for Screen { fn suspend_raw_mode(&mut self) -> HResult<()> { - self.screen - .lock()? - .suspend_raw_mode() + self.screen.lock()?.suspend_raw_mode() } fn activate_raw_mode(&mut self) -> HResult<()> { - self.screen - .lock()? - .activate_raw_mode() + self.screen.lock()?.activate_raw_mode() } } @@ -201,7 +194,7 @@ pub fn ysize() -> u16 { pub fn size() -> HResult<(usize, usize)> { let (xsize, ysize) = termion::terminal_size()?; - Ok(((xsize-1) as usize, (ysize-1) as usize)) + Ok(((xsize - 1) as usize, (ysize - 1) as usize)) } pub fn size_pixels() -> HResult<(usize, usize)> { @@ -213,31 +206,32 @@ pub fn cell_ratio() -> HResult { let (xsize, ysize) = size()?; let (xpix, ypix) = size_pixels()?; - let cell_xpix = xpix as f32 / (xsize+1) as f32; - let cell_ypix = ypix as f32 / (ysize+1) as f32; + let cell_xpix = xpix as f32 / (xsize + 1) as f32; + let cell_ypix = ypix as f32 / (ysize + 1) as f32; let ratio = cell_xpix / cell_ypix; Ok(ratio) } pub fn sized_string(string: &str, xsize: u16) -> &str { - let len = string.chars() - .map(|ch| { - if ch.is_ascii() { - (1, 1) - } else { - (UnicodeWidthChar::width(ch).unwrap_or(0), ch.len_utf8()) - } - }) - .scan((0,0), |(str_width, str_len), (ch_width, ch_len)| { - *str_width += ch_width; - *str_len += ch_len; - Some((*str_width, *str_len)) - }) - .take_while(|(str_width, _)| *str_width < xsize as usize) - .map(|(_, str_len)| str_len) - .last() - .unwrap_or(0); + let len = string + .chars() + .map(|ch| { + if ch.is_ascii() { + (1, 1) + } else { + (UnicodeWidthChar::width(ch).unwrap_or(0), ch.len_utf8()) + } + }) + .scan((0, 0), |(str_width, str_len), (ch_width, ch_len)| { + *str_width += ch_width; + *str_len += ch_len; + Some((*str_width, *str_len)) + }) + .take_while(|(str_width, _)| *str_width < xsize as usize) + .map(|(_, str_len)| str_len) + .last() + .unwrap_or(0); &string[0..len] } @@ -245,12 +239,12 @@ pub fn sized_string(string: &str, xsize: u16) -> &str { #[derive(Debug)] enum Token<'a> { Text(&'a str), - Ansi(&'a str) + Ansi(&'a str), } fn get_tokens(string: &str) -> Vec { - let mut tokens = parse_bytes(string.as_bytes()) - .fold((Vec::new(), 0), |(mut tokens, last_tok), ansi_pos| { + let mut tokens = + parse_bytes(string.as_bytes()).fold((Vec::new(), 0), |(mut tokens, last_tok), ansi_pos| { if last_tok == 0 { // first iteration if ansi_pos.start() != 0 { @@ -282,78 +276,71 @@ fn get_tokens(string: &str) -> Vec { pub fn string_len(string: &str) -> usize { let tokens = get_tokens(&string); - tokens.iter().fold(0, |len, token| { - match token { - Token::Text(text) => len + text.len(), - _ => len - } + tokens.iter().fold(0, |len, token| match token { + Token::Text(text) => len + text.len(), + _ => len, }) } - pub fn sized_string_u(string: &str, xsize: usize) -> String { let tokens = get_tokens(&string); - let sized = tokens.iter().try_fold((String::new(), 0), |(mut sized, width), token| { - let (tok, tok_width) = match token { - Token::Text(text) => { - let tok_str = text; - let tok_width = text.width(); - (tok_str, tok_width) - }, - Token::Ansi(ansi) => (ansi, 0) - }; - - // adding this token makes string larger than xsise - if width + tok_width > xsize { - let chars_left = xsize + 1 - width; - - // fill up with chars from token until xsize is reached - let fillup = tok.chars().try_fold((String::new(), 0), - |(mut fillup, fillup_width), chr| { - let chr_width = chr.width().unwrap_or(0); - - if fillup_width + chr_width > chars_left { - Err((fillup, fillup_width)) - } else { - fillup.push(chr); - Ok((fillup, fillup_width + chr_width)) + let sized = tokens + .iter() + .try_fold((String::new(), 0), |(mut sized, width), token| { + let (tok, tok_width) = match token { + Token::Text(text) => { + let tok_str = text; + let tok_width = text.width(); + (tok_str, tok_width) } - }); - - let (fillup, fillup_width) = fillup.extract(); - sized.push_str(&fillup); - - // we're done here, stop looping - Err((sized, width + fillup_width)) - } else { - sized.push_str(&tok); - Ok((sized, width + tok_width)) - } - - }); - + Token::Ansi(ansi) => (ansi, 0), + }; + + // adding this token makes string larger than xsise + if width + tok_width > xsize { + let chars_left = xsize + 1 - width; + + // fill up with chars from token until xsize is reached + let fillup = + tok.chars() + .try_fold((String::new(), 0), |(mut fillup, fillup_width), chr| { + let chr_width = chr.width().unwrap_or(0); + + if fillup_width + chr_width > chars_left { + Err((fillup, fillup_width)) + } else { + fillup.push(chr); + Ok((fillup, fillup_width + chr_width)) + } + }); + + let (fillup, fillup_width) = fillup.extract(); + sized.push_str(&fillup); + + // we're done here, stop looping + Err((sized, width + fillup_width)) + } else { + sized.push_str(&tok); + Ok((sized, width + tok_width)) + } + }); let (mut sized_str, sized_width) = sized.extract(); // pad out string if sized_width < xsize { - let padding = xsize-sized_width; + let padding = xsize - sized_width; for _ in 0..padding { sized_str += " "; } } - sized_str } - // Do these as constants - - - pub fn highlight_color() -> String { format!( "{}", @@ -395,31 +382,20 @@ pub fn color_light_yellow() -> String { } pub fn color_orange() -> String { - let color = termion::color::Fg(termion::color::AnsiValue::rgb(5 as u8 , - 4 as u8, - 0 as u8)); + let color = termion::color::Fg(termion::color::AnsiValue::rgb(5 as u8, 4 as u8, 0 as u8)); format!("{}", color) } - pub fn from_lscolor(color: &lscolors::Color) -> String { match color { - lscolors::Color::Black - => format!("{}", termion::color::Fg(termion::color::Black)), - lscolors::Color::Red - => format!("{}", termion::color::Fg(termion::color::Red)), - lscolors::Color::Green - => format!("{}", termion::color::Fg(termion::color::Green)), - lscolors::Color::Yellow - => format!("{}", termion::color::Fg(termion::color::Yellow)), - lscolors::Color::Blue - => format!("{}", termion::color::Fg(termion::color::Blue)), - lscolors::Color::Magenta - => format!("{}", termion::color::Fg(termion::color::Magenta)), - lscolors::Color::Cyan - => format!("{}", termion::color::Fg(termion::color::Cyan)), - lscolors::Color::White - => format!("{}", termion::color::Fg(termion::color::White)), + lscolors::Color::Black => format!("{}", termion::color::Fg(termion::color::Black)), + lscolors::Color::Red => format!("{}", termion::color::Fg(termion::color::Red)), + lscolors::Color::Green => format!("{}", termion::color::Fg(termion::color::Green)), + lscolors::Color::Yellow => format!("{}", termion::color::Fg(termion::color::Yellow)), + lscolors::Color::Blue => format!("{}", termion::color::Fg(termion::color::Blue)), + lscolors::Color::Magenta => format!("{}", termion::color::Fg(termion::color::Magenta)), + lscolors::Color::Cyan => format!("{}", termion::color::Fg(termion::color::Cyan)), + lscolors::Color::White => format!("{}", termion::color::Fg(termion::color::White)), _ => format!("{}", normal_color()), } } @@ -437,8 +413,8 @@ pub fn goto_xy(x: u16, y: u16) -> String { } pub fn goto_xy_u(x: usize, y: usize) -> String { - let x = (x+1) as u16; - let y = (y+1) as u16; + let x = (x + 1) as u16; + let y = (y + 1) as u16; format!("{}", termion::cursor::Goto(x, y)) } diff --git a/src/textview.rs b/src/textview.rs index b7dd0a9..fd77e3d 100644 --- a/src/textview.rs +++ b/src/textview.rs @@ -3,12 +3,11 @@ use std::io::BufRead; use strip_ansi_escapes::strip; use termion::event::Key; +use crate::dirty::Dirtyable; +use crate::fail::{HError, HResult}; use crate::files::File; use crate::term::sized_string_u; use crate::widget::{Widget, WidgetCore}; -use crate::fail::{HResult, HError}; -use crate::dirty::Dirtyable; - #[derive(Debug, PartialEq)] pub struct TextView { @@ -28,7 +27,7 @@ impl TextView { follow: false, offset: 0, file: None, - limited: false + limited: false, } } @@ -38,25 +37,26 @@ impl TextView { Ok(view) } - pub fn new_from_file_limit_lines(core: &WidgetCore, - file: &File, - num: usize) -> HResult { - let buf = std::fs::File::open(&file.path) - .map(|f| std::io::BufReader::new(f))?; - - let lines = buf.lines() - .enumerate() - .take_while(|(i, _)| num == 0 || i <= &num) - .map(|(_, l)| { - l.map_err(HError::from) - .and_then(|l| { - let l = strip(&l); - Ok(String::from_utf8_lossy(&l?).to_string()) - }) - .map_err(HError::from) - - }) - .collect::>()?; + pub fn new_from_file_limit_lines( + core: &WidgetCore, + file: &File, + num: usize, + ) -> HResult { + let buf = std::fs::File::open(&file.path).map(|f| std::io::BufReader::new(f))?; + + let lines = buf + .lines() + .enumerate() + .take_while(|(i, _)| num == 0 || i <= &num) + .map(|(_, l)| { + l.map_err(HError::from) + .and_then(|l| { + let l = strip(&l); + Ok(String::from_utf8_lossy(&l?).to_string()) + }) + .map_err(HError::from) + }) + .collect::>()?; Ok(TextView { lines: lines, @@ -64,7 +64,7 @@ impl TextView { follow: false, offset: 0, file: Some(file.clone()), - limited: true + limited: true, }) } @@ -89,9 +89,7 @@ impl TextView { if self.limited { self.file .as_ref() - .and_then(|f| { - TextView::new_from_file(&self.core, f).ok() - }) + .and_then(|f| TextView::new_from_file(&self.core, f).ok()) .map(|v| { *self = v; self.limited = false; @@ -108,10 +106,12 @@ impl TextView { let offset = self.offset as isize; let len = self.lines.len() as isize; - if len <= ysize + offset { return } + if len <= ysize + offset { + return; + } if amount > 0 { - if ysize + amount + offset + 1 >= len { + if ysize + amount + offset + 1 >= len { // Too far down self.offset = (len - ysize - 1) as usize; } else { @@ -186,40 +186,38 @@ impl Widget for TextView { let mut output = crate::term::reset(); - output += &self.lines - .iter() - .skip(self.offset) - .take(ysize as usize) - .enumerate() - .map(|(i, line)| { - format!( - "{}{}", - crate::term::goto_xy(xpos, i as u16 + ypos), - sized_string_u(&line, (xsize-1) as usize)) - }) - .collect::(); + output += &self + .lines + .iter() + .skip(self.offset) + .take(ysize as usize) + .enumerate() + .map(|(i, line)| { + format!( + "{}{}", + crate::term::goto_xy(xpos, i as u16 + ypos), + sized_string_u(&line, (xsize - 1) as usize) + ) + }) + .collect::(); Ok(output) } fn render_footer(&self) -> HResult { let (xsize, ysize) = self.core.coordinates.size_u(); let (_, ypos) = self.core.coordinates.position_u(); - let lines = self.lines - .len() - .saturating_sub(1); + let lines = self.lines.len().saturating_sub(1); let current_line_top = self.offset; - let current_line_bot = std::cmp::min(current_line_top + ysize + 1, - lines); - let line_hint = format!("{} - {} / {}", - current_line_top, - current_line_bot, - lines); + let current_line_bot = std::cmp::min(current_line_top + ysize + 1, lines); + let line_hint = format!("{} - {} / {}", current_line_top, current_line_bot, lines); let hint_xpos = xsize - line_hint.len(); let hint_ypos = ysize + ypos + 1; - let footer = format!("{}{}", - crate::term::goto_xy_u(hint_xpos, hint_ypos), - line_hint); + let footer = format!( + "{}{}", + crate::term::goto_xy_u(hint_xpos, hint_ypos), + line_hint + ); Ok(footer) } @@ -232,7 +230,7 @@ impl Widget for TextView { use crate::keybind::{Acting, Bindings, Movement}; impl Acting for TextView { - type Action=Movement; + type Action = Movement; fn search_in(&self) -> Bindings { Bindings::default() @@ -244,8 +242,18 @@ impl Acting for TextView { self.load_full(); match movement { - Up(n) => { for _ in 0..*n { self.scroll_up(); }; self.refresh()?; } - Down(n) => { for _ in 0..*n { self.scroll_down(); }; self.refresh()?; } + Up(n) => { + for _ in 0..*n { + self.scroll_up(); + } + self.refresh()?; + } + Down(n) => { + for _ in 0..*n { + self.scroll_down(); + } + self.refresh()?; + } PageUp => self.page_up(), PageDown => self.page_down(), Top => self.scroll_top(), diff --git a/src/trait_ext.rs b/src/trait_ext.rs index ba981b4..c629709 100644 --- a/src/trait_ext.rs +++ b/src/trait_ext.rs @@ -3,29 +3,20 @@ use std::path::PathBuf; use crate::fail::{HResult, MimeError}; use crate::files::File; - - - - - - - - // This makes using short-circuiting iterators more convenient pub trait ExtractResult { fn extract(self) -> T; } -impl ExtractResult for Result { +impl ExtractResult for Result { fn extract(self) -> T { match self { Ok(val) => val, - Err(val) => val + Err(val) => val, } } } - // To get MIME from Path without hassle pub trait PathBufMime { fn get_mime(&self) -> HResult; @@ -33,13 +24,10 @@ pub trait PathBufMime { impl PathBufMime for PathBuf { fn get_mime(&self) -> HResult { - let file = File::new_from_path(&self) - .map_err(|e| MimeError::AccessFailed(Box::new(e)))?; + let file = File::new_from_path(&self).map_err(|e| MimeError::AccessFailed(Box::new(e)))?; file.get_mime() - .map(|mime| { - Ok(format!("{}", mime)) - }) + .map(|mime| Ok(format!("{}", mime))) .map_err(|_| MimeError::NoMimeFound)? } } diff --git a/src/widget.rs b/src/widget.rs index 1d27dbe..6303a08 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1,22 +1,19 @@ +use std::io::{stdin, Write}; +use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex, RwLock}; -use std::sync::mpsc::{Sender, Receiver, channel}; -use std::io::{Write, stdin}; +use async_value::{Async, Stale}; use termion::event::{Event, Key, MouseEvent}; use termion::input::TermRead; -use async_value::{Async, Stale}; - +use crate::config::Config; use crate::coordinates::{Coordinates, Position, Size}; -use crate::fail::{HResult, HError, ErrorLog}; +use crate::dirty::{DirtyBit, Dirtyable}; +use crate::fail::{ErrorLog, HError, HResult}; use crate::minibuffer::MiniBuffer; +use crate::signal_notify::{notify, Signal}; use crate::term; use crate::term::{Screen, ScreenExt}; -use crate::dirty::{Dirtyable, DirtyBit}; -use crate::signal_notify::{notify, Signal}; -use crate::config::Config; - - #[derive(Debug)] pub enum Events { @@ -39,10 +36,10 @@ impl PartialEq for WidgetCore { impl std::fmt::Debug for WidgetCore { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - let output = format!("{:?}{:?}{:?}", - self.coordinates, - self.minibuffer, - self.status_bar_content); + let output = format!( + "{:?}{:?}{:?}", + self.coordinates, self.minibuffer, self.status_bar_content + ); formatter.write_str(&output) } } @@ -57,26 +54,25 @@ pub struct WidgetCore { pub status_bar_content: Arc>>, term_size: (usize, usize), dirty: DirtyBit, - pub config: Arc>> + pub config: Arc>>, } impl WidgetCore { pub fn new() -> HResult { let screen = Screen::new()?; let (xsize, ysize) = screen.size()?; - let coords = Coordinates::new_at(term::xsize(), - term::ysize() - 2, - 1, - 2); + let coords = Coordinates::new_at(term::xsize(), term::ysize() - 2, 1, 2); let (sender, receiver) = channel(); let status_bar_content = Arc::new(Mutex::new(None)); let mut config = Async::new(move |_| Ok(Config::load()?)); let confsender = sender.clone(); - config.on_ready(move |_, _| { - confsender.send(Events::ConfigLoaded).ok(); - Ok(()) - }).log(); + config + .on_ready(move |_, _| { + confsender.send(Events::ConfigLoaded).ok(); + Ok(()) + }) + .log(); config.run().log(); let core = WidgetCore { @@ -88,7 +84,8 @@ impl WidgetCore { status_bar_content: status_bar_content, term_size: (xsize, ysize), dirty: DirtyBit::new(), - config: Arc::new(RwLock::new(config)) }; + config: Arc::new(RwLock::new(config)), + }; let minibuffer = MiniBuffer::new(&core); *core.minibuffer.lock().unwrap() = Some(minibuffer); @@ -107,13 +104,13 @@ impl WidgetCore { }; let sized_status = term::sized_string_u(&status, xsize); - self.write_to_screen( - &format!( - "{}{}{}", - term::move_bottom(), - term::status_bg(), - sized_status - )).log(); + self.write_to_screen(&format!( + "{}{}{}", + term::move_bottom(), + term::status_bg(), + sized_status + )) + .log(); Ok(()) } @@ -138,16 +135,19 @@ impl WidgetCore { pub fn minibuffer_clear(&self) -> HResult<()> { self.minibuffer .lock()? - .as_mut()? + .as_mut() + .ok_or_else(|| HError::NoneError)? .clear(); Ok(()) } pub fn minibuffer(&self, query: &str) -> HResult { - let answer = self.minibuffer + let answer = self + .minibuffer .lock()? - .as_mut()? + .as_mut() + .ok_or_else(|| HError::NoneError)? .query(query, false); let mut screen = self.screen()?; screen.cursor_hide().log(); @@ -155,9 +155,11 @@ impl WidgetCore { } pub fn minibuffer_continuous(&self, query: &str) -> HResult { - let answer = self.minibuffer + let answer = self + .minibuffer .lock()? - .as_mut()? + .as_mut() + .ok_or_else(|| HError::NoneError)? .query(query, true); let mut screen = self.screen()?; screen.cursor_hide().log(); @@ -179,8 +181,8 @@ impl WidgetCore { let endpos = ypos + ysize; Ok((ypos..endpos) - .map(|line| { - format!( + .map(|line| { + format!( "{}{}{:xsize$}", crate::term::reset(), crate::term::goto_xy(xpos, line), @@ -197,15 +199,11 @@ impl WidgetCore { } pub fn config(&self) -> Config { - self.get_conf() - .unwrap_or_else(|_| Config::new()) + self.get_conf().unwrap_or_else(|_| Config::new()) } fn get_conf(&self) -> HResult { - let conf = self.config - .read()? - .get()? - .clone(); + let conf = self.config.read()?.get()?.clone(); Ok(conf) } } @@ -222,14 +220,13 @@ impl Dirtyable for WidgetCore { } } - pub trait Widget { fn get_core(&self) -> HResult<&WidgetCore>; // { - // Err(HError::NoWidgetCoreError(Backtrace::new())) - // } - fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> ;// { - // Err(HError::NoWidgetCoreError(Backtrace::new())) - // } + // Err(HError::NoWidgetCoreError(Backtrace::new())) + // } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore>; // { + // Err(HError::NoWidgetCoreError(Backtrace::new())) + // } fn get_coordinates(&self) -> HResult<&Coordinates> { Ok(&self.get_core()?.coordinates) } @@ -249,10 +246,12 @@ pub trait Widget { } fn refresh(&mut self) -> HResult<()>; fn get_drawlist(&self) -> HResult; - fn after_draw(&self) -> HResult<()> { Ok(()) } - fn config_loaded(&mut self) -> HResult<()> { Ok(()) } - - + fn after_draw(&self) -> HResult<()> { + Ok(()) + } + fn config_loaded(&mut self) -> HResult<()> { + Ok(()) + } fn on_event(&mut self, event: Event) -> HResult<()> { self.get_core()?.clear_status().log(); @@ -265,31 +264,34 @@ pub trait Widget { fn on_key(&mut self, key: Key) -> HResult<()> { match key { - _ => { self.bad(Event::Key(key))? }, + _ => self.bad(Event::Key(key))?, } Ok(()) } fn on_mouse(&mut self, event: MouseEvent) -> HResult<()> { match event { - _ => { self.bad(Event::Mouse(event)).unwrap() }, + _ => self.bad(Event::Mouse(event)).unwrap(), } Ok(()) } fn on_wtf(&mut self, event: Vec) -> HResult<()> { match event { - _ => { self.bad(Event::Unsupported(event)).unwrap() }, + _ => self.bad(Event::Unsupported(event)).unwrap(), } Ok(()) } fn bad(&mut self, event: Event) -> HResult<()> { - self.get_core()?.show_status(&format!("Stop it!! {:?} does nothing!", - event)).log(); + self.get_core()? + .show_status(&format!("Stop it!! {:?} does nothing!", event)) + .log(); if let Event::Key(key) = event { HError::undefined_key(key) - } else { Ok(()) } + } else { + Ok(()) + } } fn get_header_drawlist(&mut self) -> HResult { @@ -314,11 +316,10 @@ pub trait Widget { " ", crate::term::goto_xy(1, ypos), self.render_footer()?, - xsize = xsize as usize)) + xsize = xsize as usize + )) } - - fn get_redraw_empty_list(&self, lines: usize) -> HResult { let (xpos, ypos) = self.get_coordinates()?.u16position(); let (xsize, ysize) = self.get_coordinates()?.u16size(); @@ -340,15 +341,17 @@ pub trait Widget { // Image will draw over widget otherwise if self.get_core()?.config().graphics == "kitty" { let ypos = self.get_coordinates()?.ypos(); - print!("\x1b_Ga=d,d=y,y={}\x1b\\", ypos+1); + print!("\x1b_Ga=d,d=y,y={}\x1b\\", ypos + 1); } let result = self.run_widget(); match result { - Err(HError::RefreshParent) => {}, - _ => self.get_core()?.clear().log() + Err(HError::RefreshParent) => {} + _ => self.get_core()?.clear().log(), } - self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?; + self.get_core()? + .get_sender() + .send(Events::ExclusiveEvent(None))?; result } @@ -371,9 +374,9 @@ pub trait Widget { match event { Events::InputEvent(input) => { match self.on_event(input) { - err @ Err(HError::PopupFinnished) | - err @ Err(HError::Quit) | - err @ Err(HError::WidgetResizedError) => err?, + err @ Err(HError::PopupFinnished) + | err @ Err(HError::Quit) + | err @ Err(HError::WidgetResizedError) => err?, event @ Err(HError::MiniBufferEvent(_)) => event?, err @ Err(_) => err.log(), Ok(_) => {} @@ -393,9 +396,7 @@ pub trait Widget { _ => {} } } - Events::InputUpdated(input) => { - HError::input_updated(input)? - } + Events::InputUpdated(input) => HError::input_updated(input)?, Events::ConfigLoaded => { self.get_core_mut()?.config.write()?.pull_async()?; } @@ -408,9 +409,10 @@ pub trait Widget { Ok(()) } - fn animate_slide_up(&mut self, animator: Option<&Stale>) -> HResult<()> { - if !self.get_core()?.config().animate() { return Ok(()); } + if !self.get_core()?.config().animate() { + return Ok(()); + } let coords = self.get_coordinates()?.clone(); let xpos = coords.position().x(); @@ -420,14 +422,14 @@ pub trait Widget { let clear = self.get_core()?.get_clearlist()?; let animation_hz = self.get_core()?.config().animation_refresh_frequency as u64; - let pause_millis = 1000/animation_hz; + let pause_millis = 1000 / animation_hz; const ANIMATION_DURATION_MILLIS: u64 = 64; - let number_of_frames= (ANIMATION_DURATION_MILLIS/pause_millis) as u16; + let number_of_frames = (ANIMATION_DURATION_MILLIS / pause_millis) as u16; let pause = std::time::Duration::from_millis(pause_millis); if let Some(ref animator) = animator { if animator.is_stale()? { - return Ok(()) + return Ok(()); } } @@ -437,18 +439,21 @@ pub trait Widget { if let Some(ref animator) = animator { if animator.is_stale()? { self.set_coordinates(&coords).log(); - return Ok(()) + return Ok(()); } } - let ani_coords = Coordinates { size: Size((xsize,ysize-i)), - position: Position - ((xpos, - ypos+i)) + let ani_coords = Coordinates { + size: Size((xsize, ysize - i)), + position: Position((xpos, ypos + i)), }; self.set_coordinates(&ani_coords).log(); let buffer = self.get_drawlist()?; - if !animator.as_ref()?.is_stale()? { + if !animator + .as_ref() + .ok_or_else(|| HError::NoneError)? + .is_stale()? + { self.get_core()?.write_to_screen(&buffer).log(); } @@ -461,10 +466,9 @@ pub trait Widget { } fn draw(&mut self) -> HResult<()> { - let output = - self.get_drawlist().unwrap_or("".to_string()) + - &self.get_header_drawlist().unwrap_or("".to_string()) + - &self.get_footer_drawlist().unwrap_or("".to_string()); + let output = self.get_drawlist().unwrap_or("".to_string()) + + &self.get_header_drawlist().unwrap_or("".to_string()) + + &self.get_footer_drawlist().unwrap_or("".to_string()); self.get_core()?.write_to_screen(&output).log(); self.get_core()?.screen()?.flush().ok(); Ok(()) @@ -472,15 +476,26 @@ pub trait Widget { fn handle_input(&mut self) -> HResult<()> { let (tx_internal_event, rx_internal_event) = channel(); - let rx_global_event = self.get_core()?.event_receiver.lock()?.take()?; + let rx_global_event = self + .get_core()? + .event_receiver + .lock()? + .take() + .ok_or_else(|| HError::NoneError)?; - dispatch_events(tx_internal_event, rx_global_event, self.get_core()?.screen()?); + dispatch_events( + tx_internal_event, + rx_global_event, + self.get_core()?.screen()?, + ); for event in rx_internal_event.iter() { match event { Events::InputEvent(event) => { match self.on_event(event) { - Err(HError::Quit) => { HError::quit()?; }, + Err(HError::Quit) => { + HError::quit()?; + } _ => {} } self.get_core()?.get_sender().send(Events::RequestInput)?; @@ -511,16 +526,14 @@ pub trait Widget { if let Ok(true) = self.get_core()?.screen()?.is_resized() { let (xsize, ysize) = self.get_core()?.screen()?.get_size()?; let mut coords = self.get_core()?.coordinates.clone(); - coords.set_size_u(xsize, ysize-2); + coords.set_size_u(xsize, ysize - 2); self.set_coordinates(&coords)?; } Ok(()) } } -fn dispatch_events(tx_internal: Sender, - rx_global: Receiver, - screen: Screen) { +fn dispatch_events(tx_internal: Sender, rx_global: Receiver, screen: Screen) { let (tx_event, rx_event) = channel(); let (tx_input_req, rx_input_req) = channel(); @@ -537,7 +550,7 @@ fn dispatch_events(tx_internal: Sender, Events::ExclusiveEvent(tx_event) => { tx_exclusive_event = match tx_event { Some(locked_sender) => locked_sender.lock().unwrap().take(), - None => None + None => None, } } Events::InputEnabled(state) => { @@ -557,7 +570,7 @@ fn dispatch_events(tx_internal: Sender, } _ => {} } - if let Some(tx_exclusive) = &tx_exclusive_event { + if let Some(tx_exclusive) = &tx_exclusive_event { tx_exclusive.send(event).ok(); } else { tx_internal.send(event).ok(); @@ -566,8 +579,7 @@ fn dispatch_events(tx_internal: Sender, }); } -fn event_thread(rx_global: Receiver, - tx: Sender) { +fn event_thread(rx_global: Receiver, tx: Sender) { std::thread::spawn(move || { for event in rx_global.iter() { tx.send(event).unwrap(); @@ -578,10 +590,13 @@ fn event_thread(rx_global: Receiver, fn input_thread(tx: Sender, rx_input_request: Receiver<()>) { std::thread::spawn(move || { for input in stdin().events() { - input.map(|input| { - tx.send(Events::InputEvent(input)).unwrap(); - rx_input_request.recv().unwrap(); - }).map_err(|e| HError::from(e)).log(); + input + .map(|input| { + tx.send(Events::InputEvent(input)).unwrap(); + rx_input_request.recv().unwrap(); + }) + .map_err(|e| HError::from(e)) + .log(); } }); }