From c2de16c1e1d7cc8f5cc40f3ca977e01c6e1a9f2a Mon Sep 17 00:00:00 2001 From: Jiayi Zhao Date: Sat, 21 Mar 2020 12:39:54 -0400 Subject: wrap fs::FileType with in-house solution - This allows for efficient symlink lookup through caching - Theme processing happens upfront rather than recomputing styling over and over --- src/config/theme.rs | 54 +++++++-------- src/fs/dirlist.rs | 10 ++- src/fs/entry.rs | 119 +++++++++------------------------ src/fs/metadata.rs | 51 +++++++++++++- src/fs/mod.rs | 2 +- src/run.rs | 3 +- src/ui/widgets/tui_dirlist.rs | 55 ++++++++------- src/ui/widgets/tui_dirlist_detailed.rs | 83 +++++++++++++---------- src/ui/widgets/tui_footer.rs | 11 ++- 9 files changed, 199 insertions(+), 189 deletions(-) diff --git a/src/config/theme.rs b/src/config/theme.rs index 541c340..02f9a03 100644 --- a/src/config/theme.rs +++ b/src/config/theme.rs @@ -1,7 +1,7 @@ use serde_derive::Deserialize; use std::collections::HashMap; -use tui::style::Color; +use tui::style::{Color, Modifier, Style}; use crate::THEME_FILE; @@ -51,14 +51,21 @@ impl JoshutoStyleThemeRaw { let bg = Self::str_to_color(self.bg.as_str()); let fg = Self::str_to_color(self.fg.as_str()); - JoshutoStyleTheme { - bg, - fg, - bold: self.bold, - underline: self.underline, - invert: self.invert, - prefix: self.prefix.clone(), + let mut modifier = Modifier::empty(); + if self.bold { + modifier.insert(Modifier::BOLD); } + if self.underline { + modifier.insert(Modifier::UNDERLINED); + } + if self.invert { + modifier.insert(Modifier::REVERSED); + } + + JoshutoStyleTheme::default() + .set_fg(fg) + .set_bg(bg) + .insert(modifier) } pub fn str_to_color(s: &str) -> Color { @@ -162,9 +169,7 @@ impl Flattenable for JoshutoRawTheme { pub struct JoshutoStyleTheme { pub fg: Color, pub bg: Color, - pub bold: bool, - pub underline: bool, - pub invert: bool, + pub modifier: Modifier, pub prefix: Option, } @@ -177,16 +182,13 @@ impl JoshutoStyleTheme { self.fg = fg; self } - pub fn set_bold(mut self, bold: bool) -> Self { - self.bold = bold; + pub fn set_prefix(mut self, prefix: JoshutoPrefix) -> Self { + self.prefix = Some(prefix); self } - pub fn set_underline(mut self, bold: bool) -> Self { - self.bold = bold; - self - } - pub fn set_invert(mut self, bold: bool) -> Self { - self.bold = bold; + + pub fn insert(mut self, modifier: Modifier) -> Self { + self.modifier.insert(modifier); self } } @@ -196,9 +198,7 @@ impl std::default::Default for JoshutoStyleTheme { Self { fg: default_color(), bg: default_color(), - bold: false, - underline: false, - invert: false, + modifier: Modifier::empty(), prefix: None, } } @@ -226,20 +226,20 @@ impl std::default::Default for JoshutoTheme { fn default() -> Self { let selection = JoshutoStyleTheme::default() .set_fg(Color::LightYellow) - .set_bold(true); + .insert(Modifier::BOLD); let executable = JoshutoStyleTheme::default() .set_fg(Color::LightGreen) - .set_bold(true); + .insert(Modifier::BOLD); let regular = JoshutoStyleTheme::default().set_fg(Color::White); let directory = JoshutoStyleTheme::default() .set_fg(Color::LightBlue) - .set_bold(true); + .insert(Modifier::BOLD); let link = JoshutoStyleTheme::default() .set_fg(Color::LightCyan) - .set_bold(true); + .insert(Modifier::BOLD); let socket = JoshutoStyleTheme::default() .set_fg(Color::LightMagenta) - .set_bold(true); + .insert(Modifier::BOLD); let ext = HashMap::new(); Self { diff --git a/src/fs/dirlist.rs b/src/fs/dirlist.rs index d62664c..d636b2b 100644 --- a/src/fs/dirlist.rs +++ b/src/fs/dirlist.rs @@ -139,8 +139,14 @@ fn map_entry_default(result: std::io::Result) -> Option match JoshutoDirEntry::from(&direntry) { Ok(s) => Some(s), - Err(_) => None, + Err(e) => { + eprintln!("Entry: {:?}, {:?}", direntry, e); + None + } }, - Err(_) => None, + Err(e) => { + eprintln!("{:?}", e); + None + } } } diff --git a/src/fs/entry.rs b/src/fs/entry.rs index 3cba354..80847b4 100644 --- a/src/fs/entry.rs +++ b/src/fs/entry.rs @@ -68,7 +68,7 @@ impl JoshutoDirEntry { pub fn get_fg_color(&self) -> Color { let metadata = &self.metadata; - let filetype = metadata.file_type; + let filetype = &metadata.file_type; if self.is_selected() { THEME_T.selection.fg @@ -92,7 +92,7 @@ impl JoshutoDirEntry { pub fn get_bg_color(&self) -> Color { let metadata = &self.metadata; - let filetype = metadata.file_type; + let filetype = &metadata.file_type; if self.is_selected() { THEME_T.selection.bg @@ -116,119 +116,62 @@ impl JoshutoDirEntry { pub fn get_modifier(&self) -> Modifier { let metadata = &self.metadata; - let filetype = metadata.file_type; - - let mut modifier = Modifier::empty(); + let filetype = &metadata.file_type; if filetype.is_dir() { - if THEME_T.directory.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.directory.underline { - modifier.insert(Modifier::UNDERLINED); - } + THEME_T.directory.modifier } else if filetype.is_symlink() { - if THEME_T.link.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.link.underline { - modifier.insert(Modifier::UNDERLINED); - } + THEME_T.link.modifier } else { match self.file_path().extension() { - None => {} + None => Modifier::empty(), Some(os_str) => match os_str.to_str() { - None => {} + None => Modifier::empty(), Some(s) => match THEME_T.ext.get(s) { - None => {} - Some(t) => { - if t.bold { - modifier.insert(Modifier::BOLD); - } - if t.underline { - modifier.insert(Modifier::UNDERLINED); - } - } + None => Modifier::empty(), + Some(t) => t.modifier, }, }, - }; + } } - modifier } pub fn get_style(&self) -> Style { let metadata = &self.metadata; - let filetype = metadata.file_type; - - let mut style = Style::default(); + let filetype = &metadata.file_type; if self.is_selected() { - let mut modifier = Modifier::empty(); - if THEME_T.selection.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.selection.underline { - modifier.insert(Modifier::UNDERLINED); - } - - style = style.fg(THEME_T.selection.fg).bg(THEME_T.selection.bg); - style = style.modifier(modifier); + Style::default() + .fg(THEME_T.selection.fg) + .bg(THEME_T.selection.bg) + .modifier(THEME_T.selection.modifier) } else if filetype.is_dir() { - let mut modifier = Modifier::empty(); - if THEME_T.directory.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.directory.underline { - modifier.insert(Modifier::UNDERLINED); - } - - style = style.fg(THEME_T.directory.fg).bg(THEME_T.directory.bg); - style = style.modifier(modifier); + Style::default() + .fg(THEME_T.directory.fg) + .bg(THEME_T.directory.bg) + .modifier(THEME_T.directory.modifier) } else if filetype.is_symlink() { - let mut modifier = Modifier::empty(); - if THEME_T.link.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.link.underline { - modifier.insert(Modifier::UNDERLINED); - } - - style = style.fg(THEME_T.link.fg).bg(THEME_T.link.bg); - style = style.modifier(modifier); + Style::default() + .fg(THEME_T.link.fg) + .bg(THEME_T.link.bg) + .modifier(THEME_T.link.modifier) } else if unix::is_executable(metadata.mode) { - let mut modifier = Modifier::empty(); - if THEME_T.link.bold { - modifier.insert(Modifier::BOLD); - } - if THEME_T.link.underline { - modifier.insert(Modifier::UNDERLINED); - } - - style = style.fg(THEME_T.executable.fg).bg(THEME_T.executable.bg); - style = style.modifier(modifier); + Style::default() + .fg(THEME_T.executable.fg) + .bg(THEME_T.executable.bg) + .modifier(THEME_T.executable.modifier) } else { match self.file_path().extension() { - None => {} + None => Style::default(), Some(os_str) => match os_str.to_str() { - None => {} + None => Style::default(), Some(s) => match THEME_T.ext.get(s) { - None => {} - Some(t) => { - let mut modifier = Modifier::empty(); - if t.bold { - modifier.insert(Modifier::BOLD); - } - if t.underline { - modifier.insert(Modifier::UNDERLINED); - } - style = style.fg(t.fg).bg(t.bg); - style = style.modifier(modifier); - } + None => Style::default(), + Some(t) => Style::default().fg(t.fg).bg(t.bg).modifier(t.modifier), }, }, } } - style } } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 9f3ddd9..9a3fb60 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,11 +1,41 @@ use std::{fs, path, process, time}; +#[derive(Clone, Debug)] +pub enum FileType { + Directory, + Symlink(String), + File, +} + +impl FileType { + pub fn is_dir(&self) -> bool { + match *self { + Self::Directory => true, + _ => false, + } + } + + pub fn is_symlink(&self) -> bool { + match *self { + Self::Symlink(_) => true, + _ => false, + } + } + + pub fn is_file(&self) -> bool { + match *self { + Self::File => true, + _ => false, + } + } +} + #[derive(Clone, Debug)] pub struct JoshutoMetadata { pub len: u64, pub modified: time::SystemTime, pub permissions: fs::Permissions, - pub file_type: fs::FileType, + pub file_type: FileType, pub mimetype: Option, #[cfg(unix)] pub uid: u32, @@ -26,9 +56,24 @@ impl JoshutoMetadata { let modified = metadata.modified()?; let permissions = metadata.permissions(); let file_type = metadata.file_type(); - let mut mimetype = None; - if file_type.is_file() { + let file_type = if file_type.is_dir() { + FileType::Directory + } else if file_type.is_symlink() { + let mut link = "".to_string(); + + if let Ok(path) = fs::read_link(path) { + if let Some(s) = path.to_str() { + link = s.to_string(); + } + } + FileType::Symlink(link) + } else { + FileType::File + }; + + let mut mimetype = None; + if let FileType::File = file_type { #[cfg(feature = "file_mimetype")] { mimetype = file_mimetype(path) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 632c2ff..2704bdc 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -4,4 +4,4 @@ mod metadata; pub use self::dirlist::JoshutoDirList; pub use self::entry::JoshutoDirEntry; -pub use self::metadata::JoshutoMetadata; +pub use self::metadata::{FileType, JoshutoMetadata}; diff --git a/src/run.rs b/src/run.rs index 65f52c8..23002da 100644 --- a/src/run.rs +++ b/src/run.rs @@ -63,7 +63,8 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io: let size_string = format::file_size_to_string(s); format!( "io_worker completed successfully: {} processed", - size_string) + size_string + ) } Err(e) => format!("io_worker was not completed: {}", e.to_string()), }; diff --git a/src/ui/widgets/tui_dirlist.rs b/src/ui/widgets/tui_dirlist.rs index 5eeb23b..8840bda 100644 --- a/src/ui/widgets/tui_dirlist.rs +++ b/src/ui/widgets/tui_dirlist.rs @@ -54,12 +54,13 @@ impl<'a> Widget for TuiDirList<'a> { let name = entry.file_name(); let name_width = name.width(); - let mut style = entry.get_style(); - if i == screen_index { - style = style.modifier(Modifier::REVERSED); - } + let style = if i == screen_index { + entry.get_style().modifier(Modifier::REVERSED) + } else { + entry.get_style() + }; - let file_type = entry.metadata.file_type; + let file_type = &entry.metadata.file_type; if file_type.is_dir() { if name_width <= area_width { buf.set_stringn(x, y + i as u16, name, area_width, style); @@ -67,27 +68,33 @@ impl<'a> Widget for TuiDirList<'a> { buf.set_stringn(x, y + i as u16, name, area_width - 1, style); buf.set_string(x + area_width as u16 - 1, y + i as u16, "…", style); } - continue; - } - if name_width < area_width { - buf.set_stringn(x, y + i as u16, name, area_width, style); } else { - match name.rfind('.') { - None => { - buf.set_stringn(x, y + i as u16, name, area_width, style); - } - Some(p_ind) => { - let ext_width = name[p_ind..].width(); - let file_name_width = area_width - ext_width - 1; + if name_width < area_width { + buf.set_stringn(x, y + i as u16, name, area_width, style); + } else { + match name.rfind('.') { + None => { + buf.set_stringn(x, y + i as u16, name, area_width, style); + } + Some(p_ind) => { + let ext_width = name[p_ind..].width(); + let file_name_width = area_width - ext_width - 1; - buf.set_stringn(x, y + i as u16, &name[..p_ind], file_name_width, style); - buf.set_string(x + file_name_width as u16, y + i as u16, "…", style); - buf.set_string( - x + file_name_width as u16 + 1, - y + i as u16, - &name[p_ind..], - style, - ); + buf.set_stringn( + x, + y + i as u16, + &name[..p_ind], + file_name_width, + style, + ); + buf.set_string(x + file_name_width as u16, y + i as u16, "…", style); + buf.set_string( + x + file_name_width as u16 + 1, + y + i as u16, + &name[p_ind..], + style, + ); + } } } } diff --git a/src/ui/widgets/tui_dirlist_detailed.rs b/src/ui/widgets/tui_dirlist_detailed.rs index 5ec8633..ac7da28 100644 --- a/src/ui/widgets/tui_dirlist_detailed.rs +++ b/src/ui/widgets/tui_dirlist_detailed.rs @@ -21,11 +21,7 @@ impl<'a> TuiDirListDetailed<'a> { impl<'a> Widget for TuiDirListDetailed<'a> { fn draw(&mut self, area: Rect, buf: &mut Buffer) { - if area.width < 1 || area.height < 1 { - return; - } - - if area.width < 4 { + if area.width < 4 || area.height < 1 { return; } @@ -58,12 +54,13 @@ impl<'a> Widget for TuiDirListDetailed<'a> { let name = entry.file_name(); let name_width = name.width(); - let mut style = entry.get_style(); - if i == screen_index { - style = style.modifier(Modifier::REVERSED); - } + let style = if i == screen_index { + entry.get_style().modifier(Modifier::REVERSED) + } else { + entry.get_style() + }; - let file_type = entry.metadata.file_type; + let file_type = &entry.metadata.file_type; if file_type.is_dir() { if name_width <= area_width { buf.set_stringn(x, y + i as u16, name, area_width, style); @@ -71,38 +68,50 @@ impl<'a> Widget for TuiDirListDetailed<'a> { buf.set_stringn(x, y + i as u16, name, area_width - 1, style); buf.set_string(x + area_width as u16 - 1, y + i as u16, "…", style); } - continue; - } - - if name_width < area_width - FILE_SIZE_WIDTH { - buf.set_stringn(x, y + i as u16, name, area_width - FILE_SIZE_WIDTH, style); + // } else if file_type.is_symlink() { } else { - match name.rfind('.') { - None => { - buf.set_stringn(x, y + i as u16, name, area_width - FILE_SIZE_WIDTH, style); - } - Some(p_ind) => { - let ext_width = name[p_ind..].width(); - let file_name_width = area_width - FILE_SIZE_WIDTH - ext_width - 2; + if name_width < area_width - FILE_SIZE_WIDTH { + buf.set_stringn(x, y + i as u16, name, area_width - FILE_SIZE_WIDTH, style); + } else { + match name.rfind('.') { + None => { + buf.set_stringn( + x, + y + i as u16, + name, + area_width - FILE_SIZE_WIDTH, + style, + ); + } + Some(p_ind) => { + let ext_width = name[p_ind..].width(); + let file_name_width = area_width - FILE_SIZE_WIDTH - ext_width - 2; - buf.set_stringn(x, y + i as u16, &name[..p_ind], file_name_width, style); - buf.set_string(x + file_name_width as u16, y + i as u16, "…", style); - buf.set_string( - x + file_name_width as u16 + 1, - y + i as u16, - &name[p_ind..], - style, - ); + buf.set_stringn( + x, + y + i as u16, + &name[..p_ind], + file_name_width, + style, + ); + buf.set_string(x + file_name_width as u16, y + i as u16, "…", style); + buf.set_string( + x + file_name_width as u16 + 1, + y + i as u16, + &name[p_ind..], + style, + ); + } } } + let file_size_string = format::file_size_to_string(entry.metadata.len); + buf.set_string( + x + (area_width - FILE_SIZE_WIDTH) as u16, + y + i as u16, + file_size_string, + style, + ); } - let file_size_string = format::file_size_to_string(entry.metadata.len); - buf.set_string( - x + (area_width - FILE_SIZE_WIDTH) as u16, - y + i as u16, - file_size_string, - style, - ); } } } diff --git a/src/ui/widgets/tui_footer.rs b/src/ui/widgets/tui_footer.rs index e957590..83598a2 100644 --- a/src/ui/widgets/tui_footer.rs +++ b/src/ui/widgets/tui_footer.rs @@ -5,7 +5,7 @@ use tui::layout::Rect; use tui::style::{Color, Style}; use tui::widgets::{Paragraph, Text, Widget}; -use crate::fs::JoshutoDirEntry; +use crate::fs::{FileType, JoshutoDirEntry}; use crate::util::format; pub struct TuiFooter<'a> { @@ -51,13 +51,12 @@ impl<'a> Widget for TuiFooter<'a> { Text::raw(mimetype), ]; - if self.entry.metadata.file_type.is_symlink() { - if let Ok(path) = fs::read_link(self.entry.file_path()) { + match &self.entry.metadata.file_type { + FileType::Symlink(s) => { text.push(Text::styled(" -> ", mode_style)); - if let Some(s) = path.to_str() { - text.push(Text::styled(s.to_string(), mode_style)); - } + text.push(Text::styled(s, mode_style)); } + _ => {} } Paragraph::new(text.iter()).wrap(true).draw(area, buf); -- cgit v1.2.3