diff options
author | DLFW <daniel@llin.info> | 2021-06-20 01:14:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-19 19:14:10 -0400 |
commit | e6359a599e804031189eb9bc067c4eff9b01a1a8 (patch) | |
tree | f94102b28145f91db5b0bded5e38869157c1946f /src/util | |
parent | 362a507ad60ee479fb68a48fc7daf4a49652f1bf (diff) |
Show symlinked files and dirs correctly (#72)
* Treat symlinks as normal files and dirs
* Dirs are recognized as dirs no matter is they are a symlink
(correct devicon)
* Permission flags shown in the footer are the permissions of the target
in case a symlink is selected
* Size of a symlinked file is the size of the target
* File display to be fixed: symlink arrow now just overrides the file
size, but both need to be shown one after the other
* Correctly show symlink arrow and file size next
This commit includes quite some refactoring. The shortening of the
left and right part of an entry in a dir list and the shortening of the
file name are separated into functions which don't directly write to the
buffer but just return strings. That way, they get testable and further
enhancements like different line modes should be easier to implement.
* fix review findings
* better file name truncation
File names are only truncated at grapheme border, while the width is
calculated on the actual nuber of terminal cells.
* more adequate test organization
* more code structure
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/mod.rs | 1 | ||||
-rw-r--r-- | src/util/string.rs | 67 | ||||
-rw-r--r-- | src/util/style.rs | 46 |
3 files changed, 98 insertions, 16 deletions
diff --git a/src/util/mod.rs b/src/util/mod.rs index 35f264c..53b5512 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -10,6 +10,7 @@ pub mod name_resolution; pub mod search; pub mod select; pub mod sort; +pub mod string; pub mod style; pub mod to_string; pub mod unix; diff --git a/src/util/string.rs b/src/util/string.rs new file mode 100644 index 0000000..e2e0002 --- /dev/null +++ b/src/util/string.rs @@ -0,0 +1,67 @@ +use unicode_segmentation::UnicodeSegmentation; +use unicode_width::UnicodeWidthStr; + +///Truncates a string to width, less or equal to the specified one. +/// +///In case the point of truncation falls into a full-width character, +///the returned string will be shorter than the given `width`. +///Otherwise, it will be equal. +pub trait UnicodeTruncate { + fn trunc(&self, width: usize) -> String; +} + +impl UnicodeTruncate for str { + #[inline] + fn trunc(&self, width: usize) -> String { + if self.width() <= width { + String::from(self) + } else { + let mut length: usize = 0; + let mut result = String::new(); + for grapheme in self.graphemes(true) { + let grapheme_length = grapheme.width(); + length += grapheme_length; + if length > width { + break; + }; + result.push_str(grapheme); + } + result + } + } +} + +#[cfg(test)] +mod tests_trunc { + use super::UnicodeTruncate; + + #[test] + fn truncate_correct_despite_several_multibyte_chars() { + assert_eq!(String::from("r͂o͒͜w̾").trunc(2), String::from("r͂o͒͜")); + } + + #[test] + fn truncate_at_end_returns_complete_string() { + assert_eq!(String::from("r͂o͒͜w̾").trunc(3), String::from("r͂o͒͜w̾")); + } + + #[test] + fn truncate_behind_end_returns_complete_string() { + assert_eq!(String::from("r͂o͒͜w̾").trunc(4), String::from("r͂o͒͜w̾")); + } + + #[test] + fn truncate_at_zero_returns_empty_string() { + assert_eq!(String::from("r͂o͒͜w̾").trunc(0), String::from("")); + } + + #[test] + fn truncate_correct_despite_fullwidth_character() { + assert_eq!(String::from("a🌕bc").trunc(4), String::from("a🌕b")); + } + + #[test] + fn truncate_within_fullwidth_character_truncates_before_the_character() { + assert_eq!(String::from("a🌕").trunc(2), String::from("a")); + } +} diff --git a/src/util/style.rs b/src/util/style.rs index e04daf0..69a3704 100644 --- a/src/util/style.rs +++ b/src/util/style.rs @@ -1,6 +1,6 @@ use tui::style::Style; -use crate::fs::{FileType, JoshutoDirEntry}; +use crate::fs::{FileType, JoshutoDirEntry, LinkType}; use crate::util::unix; use crate::THEME_T; @@ -8,25 +8,39 @@ use crate::THEME_T; pub fn entry_style(entry: &JoshutoDirEntry) -> Style { let metadata = &entry.metadata; let filetype = &metadata.file_type(); + let linktype = &metadata.link_type(); - match filetype { - _ if entry.is_selected() => Style::default() + if entry.is_selected() { + Style::default() .fg(THEME_T.selection.fg) .bg(THEME_T.selection.bg) - .add_modifier(THEME_T.selection.modifier), - FileType::Directory => Style::default() - .fg(THEME_T.directory.fg) - .bg(THEME_T.directory.bg) - .add_modifier(THEME_T.directory.modifier), - FileType::Symlink(_) => Style::default() - .fg(THEME_T.link.fg) - .bg(THEME_T.link.bg) - .add_modifier(THEME_T.link.modifier), - _ if unix::is_executable(metadata.mode) => Style::default() + .add_modifier(THEME_T.selection.modifier) + } else { + match linktype { + LinkType::Symlink(_) => Style::default() + .fg(THEME_T.link.fg) + .bg(THEME_T.link.bg) + .add_modifier(THEME_T.link.modifier), + LinkType::Normal => match filetype { + FileType::Directory => Style::default() + .fg(THEME_T.directory.fg) + .bg(THEME_T.directory.bg) + .add_modifier(THEME_T.directory.modifier), + FileType::File => file_style(entry), + }, + } + } +} + +fn file_style(entry: &JoshutoDirEntry) -> Style { + let metadata = &entry.metadata; + if unix::is_executable(metadata.mode) { + Style::default() .fg(THEME_T.executable.fg) .bg(THEME_T.executable.bg) - .add_modifier(THEME_T.executable.modifier), - _ => match entry.file_path().extension() { + .add_modifier(THEME_T.executable.modifier) + } else { + match entry.file_path().extension() { None => Style::default(), Some(os_str) => match os_str.to_str() { None => Style::default(), @@ -35,6 +49,6 @@ pub fn entry_style(entry: &JoshutoDirEntry) -> Style { Some(t) => Style::default().fg(t.fg).bg(t.bg).add_modifier(t.modifier), }, }, - }, + } } } |