diff options
Diffstat (limited to 'src/layout.rs')
-rw-r--r-- | src/layout.rs | 338 |
1 files changed, 211 insertions, 127 deletions
diff --git a/src/layout.rs b/src/layout.rs index 4205f89..cf4af93 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,9 +1,18 @@ +use super::session::SortKey; + use super::config::*; use super::errors::FxError; use super::functions::*; -use super::state::{FileType, ItemInfo, BEGINNING_ROW}; +use super::nums::*; +use super::state::{ItemInfo, BEGINNING_ROW}; use super::term::*; +use serde::{Deserialize, Serialize}; +use syntect::easy::HighlightLines; +use syntect::highlighting::Theme; +use syntect::parsing::SyntaxSet; +use syntect::util::{as_24_bit_terminal_escaped, split_at, LinesWithEndings}; + /// cf: https://docs.rs/image/latest/src/image/image.rs.html#84-112 pub const MAX_SIZE_TO_PREVIEW: u64 = 1_000_000_000; pub const IMAGE_EXTENSION: [&str; 20] = [ @@ -15,25 +24,30 @@ pub const CHAFA_WARNING: &str = pub const PROPER_WIDTH: u16 = 28; pub const TIME_WIDTH: u16 = 16; -pub const DEFAULT_NAME_LENGTH: u16 = 30; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Layout { + pub nums: Num, pub y: u16, pub terminal_row: u16, pub terminal_column: u16, pub name_max_len: usize, pub time_start_pos: u16, - pub use_full: Option<bool>, - pub option_name_len: Option<usize>, pub colors: ConfigColor, + pub sort_by: SortKey, + pub show_hidden: bool, pub preview: bool, - pub preview_start_column: u16, - pub preview_width: u16, + pub split: Split, + pub preview_start: (u16, u16), + pub preview_space: (u16, u16), + pub syntax_highlight: bool, + pub syntax_set: SyntaxSet, + pub theme: Theme, pub has_chafa: bool, pub is_kitty: bool, } +#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)] pub enum PreviewType { NotReadable, TooBigSize, @@ -43,25 +57,38 @@ pub enum PreviewType { Binary, } +#[derive(Debug, PartialEq, Deserialize, Serialize, Clone, Copy)] +pub enum Split { + Vertical, + Horizontal, +} + impl Layout { /// Print preview according to the preview type. pub fn print_preview(&self, item: &ItemInfo, y: u16) { - //At least print the item name - self.print_file_name(item); - //Clear preview space - self.clear_preview(self.preview_start_column); + match self.split { + Split::Vertical => { + //At least print the item name + self.print_file_name(item); + //Clear preview space + self.clear_preview(self.preview_start.0); + } + Split::Horizontal => { + self.clear_preview(self.preview_start.1); + } + } - match check_preview_type(item) { - PreviewType::NotReadable => { + match item.preview_type { + Some(PreviewType::NotReadable) => { print!("(file not readable)"); } - PreviewType::TooBigSize => { + Some(PreviewType::TooBigSize) => { print!("(file too big for preview)"); } - PreviewType::Directory => { - self.preview_content(item, true); + Some(PreviewType::Directory) => { + self.preview_directory(item); } - PreviewType::Image => { + Some(PreviewType::Image) => { if self.has_chafa { if let Err(e) = self.preview_image(item, y) { print_warning(e, y); @@ -69,7 +96,7 @@ impl Layout { } else { let help = format_txt(CHAFA_WARNING, self.terminal_column - 1, false); for (i, line) in help.iter().enumerate() { - move_to(self.preview_start_column, BEGINNING_ROW + i as u16); + move_to(self.preview_start.0, BEGINNING_ROW + i as u16); print!("{}", line,); if BEGINNING_ROW + i as u16 == self.terminal_row - 1 { break; @@ -77,73 +104,170 @@ impl Layout { } } } - PreviewType::Text => { - self.preview_content(item, false); + Some(PreviewType::Text) => { + if self.syntax_highlight { + match self.preview_text_with_sh(item) { + Ok(_) => {} + Err(e) => { + print!("{}", e); + } + } + } else { + self.preview_text(item); + } } - PreviewType::Binary => { + Some(PreviewType::Binary) => { print!("(Binary file)"); } + _ => { + print!("(Not Available)"); + } } } /// Print item name at the top. fn print_file_name(&self, item: &ItemInfo) { - move_to(self.preview_start_column, 1); + move_to(self.preview_start.0 - 1, 1); clear_until_newline(); + move_right(1); let mut file_name = format!("[{}]", item.file_name); - if file_name.len() > self.preview_width.into() { - file_name = file_name.chars().take(self.preview_width.into()).collect(); + if file_name.len() > self.preview_space.0.into() { + file_name = file_name + .chars() + .take(self.preview_space.0.into()) + .collect(); } print!("{}", file_name); } - /// Print text preview on the right half of the terminal. - fn preview_content(&self, item: &ItemInfo, is_dir: bool) { - let content = if is_dir { + fn preview_text(&self, item: &ItemInfo) { + let content = { + if let Some(content) = &item.content { + format_txt(content, self.preview_space.0, false) + } else { + vec![] + } + }; + self.print_txt_in_preview_area(item, content, false); + } + + /// Preview text with syntax highlighting. + fn preview_text_with_sh(&self, item: &ItemInfo) -> Result<(), FxError> { + if let Ok(Some(syntax)) = self.syntax_set.find_syntax_for_file(item.file_path.clone()) { + let mut h = HighlightLines::new(syntax, &self.theme); + if let Some(content) = &item.content { + move_to(self.preview_start.0, BEGINNING_ROW); + let mut result = vec![]; + for (index, line) in LinesWithEndings::from(content).enumerate() { + let count = line.len() / self.preview_space.0 as usize; + let mut range = h.highlight_line(line, &self.syntax_set)?; + for _ in 0..=count + 1 { + let ranges = split_at(&range, (self.preview_space.0) as usize); + if !ranges.0.is_empty() { + result.push(ranges.0); + } + range = ranges.1; + } + if index > self.preview_space.1 as usize + item.preview_scroll { + break; + } + } + let result: Vec<String> = result + .iter() + .map(|x| as_24_bit_terminal_escaped(x, false)) + .collect(); + self.print_txt_in_preview_area(item, result, true); + } else { + print!(""); + } + } else { + self.preview_text(item); + } + Ok(()) + } + + fn preview_directory(&self, item: &ItemInfo) { + let content = { let contents = match &item.symlink_dir_path { None => list_up_contents(&item.file_path), Some(p) => list_up_contents(p), }; if let Ok(contents) = contents { if let Ok(contents) = make_tree(contents) { - format_txt(&contents, self.preview_width, false) + format_txt(&contents, self.preview_space.0, false) } else { vec![] } } else { vec![] } - } else { - let item = item.file_path.clone(); - let column = self.terminal_column; - let content = std::fs::read_to_string(item); - if let Ok(content) = content { - let content = content.replace('\t', " "); - format_txt(&content, column - 1, false) - } else { - vec![] - } }; - //Print preview (wrapping) - for (i, line) in content.iter().enumerate() { - move_to(self.preview_start_column, BEGINNING_ROW + i as u16); - set_color(&TermColor::ForeGround(&Colorname::LightBlack)); - print!("{}", line); - reset_color(); - if BEGINNING_ROW + i as u16 == self.terminal_row - 1 { - break; + self.print_txt_in_preview_area(item, content, false); + } + + fn print_txt_in_preview_area( + &self, + item: &ItemInfo, + content: Vec<String>, + syntex_highlight: bool, + ) { + match self.split { + Split::Vertical => { + for (i, line) in content.iter().enumerate() { + if i < item.preview_scroll { + continue; + } + let sum = (i - item.preview_scroll) as u16; + let row = self.preview_start.1 + sum as u16; + move_to(self.preview_start.0, row); + if syntex_highlight { + print!("{}", line); + } else { + set_color(&TermColor::ForeGround(&Colorname::LightBlack)); + print!("{}", line); + reset_color(); + } + if sum == self.preview_space.1 - 1 { + reset_color(); + break; + } + } + } + Split::Horizontal => { + for (i, line) in content.iter().enumerate() { + if i < item.preview_scroll { + continue; + } + let sum = (i - item.preview_scroll) as u16; + let row = self.preview_start.1 + sum as u16; + move_to(1, row); + if syntex_highlight { + print!("{}", line); + } else { + set_color(&TermColor::ForeGround(&Colorname::LightBlack)); + print!("{}", line); + reset_color(); + } + if row == self.terminal_row + self.preview_space.1 { + reset_color(); + break; + } + } } } } /// Print text preview on the right half of the terminal (Experimental). fn preview_image(&self, item: &ItemInfo, y: u16) -> Result<(), FxError> { - let wxh = format!( - "--size={}x{}", - self.preview_width, - self.terminal_row - BEGINNING_ROW - ); + let wxh = match self.split { + Split::Vertical => { + format!("--size={}x{}", self.preview_space.0, self.preview_space.1) + } + Split::Horizontal => { + format!("--size={}x{}", self.preview_space.0, self.preview_space.1) + } + }; let file_path = item.file_path.to_str(); if file_path.is_none() { @@ -155,31 +279,50 @@ impl Layout { .args(["--animate=false", &wxh, file_path.unwrap()]) .output()? .stdout; - let output = String::from_utf8(output).unwrap(); - for (i, line) in output.lines().enumerate() { - let next_line: u16 = BEGINNING_ROW + (i as u16) + 1; - print!("{}", line); - move_to(self.preview_start_column, next_line); + let output = String::from_utf8(output)?; + + match self.split { + Split::Vertical => { + for (i, line) in output.lines().enumerate() { + print!("{}", line); + let next_line: u16 = BEGINNING_ROW + (i as u16) + 1; + move_to(self.preview_start.0, next_line); + } + } + Split::Horizontal => { + for (i, line) in output.lines().enumerate() { + print!("{}", line); + let next_line: u16 = self.preview_start.1 + (i as u16) + 1; + move_to(1, next_line); + } + } } Ok(()) } /// Clear the preview space. - fn clear_preview(&self, preview_start_column: u16) { - for i in 0..self.terminal_row { - move_to(preview_start_column, BEGINNING_ROW + i as u16); - clear_until_newline(); + fn clear_preview(&self, preview_start_point: u16) { + match self.split { + Split::Vertical => { + for i in 0..=self.terminal_row { + move_to(preview_start_point, BEGINNING_ROW + i as u16); + clear_until_newline(); + } + move_to(self.preview_start.0, BEGINNING_ROW); + } + Split::Horizontal => { + for i in 0..=self.terminal_row { + move_to(1, preview_start_point + i as u16); + clear_until_newline(); + } + move_to(1, preview_start_point); + } } - move_to(self.preview_start_column, BEGINNING_ROW); } } /// Make app's layout according to terminal width and app's config. -pub fn make_layout( - column: u16, - use_full: Option<bool>, - name_length: Option<usize>, -) -> (u16, usize) { +pub fn make_layout(column: u16) -> (u16, usize) { let mut time_start: u16; let mut name_max: usize; @@ -188,30 +331,8 @@ pub fn make_layout( name_max = (column - 2).into(); (time_start, name_max) } else { - match use_full { - Some(true) | None => { - time_start = column - TIME_WIDTH; - name_max = (time_start - SPACES).into(); - } - Some(false) => match name_length { - Some(option_max) => { - time_start = option_max as u16 + SPACES; - name_max = option_max; - } - None => { - time_start = if column >= DEFAULT_NAME_LENGTH + TIME_WIDTH + SPACES { - DEFAULT_NAME_LENGTH + SPACES - } else { - column - TIME_WIDTH - }; - name_max = if column >= DEFAULT_NAME_LENGTH + TIME_WIDTH + SPACES { - DEFAULT_NAME_LENGTH.into() - } else { - (time_start - SPACES).into() - }; - } - }, - } + time_start = column - TIME_WIDTH; + name_max = (time_start - SPACES).into(); let required = time_start + TIME_WIDTH - 1; if required > column { let diff = required - column; @@ -222,40 +343,3 @@ pub fn make_layout( (time_start, name_max) } } - -fn check_preview_content_type(item: &ItemInfo) -> PreviewType { - if item.file_size > MAX_SIZE_TO_PREVIEW { - PreviewType::TooBigSize - } else if is_supported_ext(item) { - PreviewType::Image - } else if let Ok(content) = &std::fs::read(&item.file_path) { - if content_inspector::inspect(content).is_text() { - PreviewType::Text - } else { - PreviewType::Binary - } - } else { - // failed to resolve item to any form of supported preview - // it is probably not accessible due to permissions, broken symlink etc. - PreviewType::NotReadable - } -} - -/// Check preview type. -fn check_preview_type(item: &ItemInfo) -> PreviewType { - if item.file_type == FileType::Directory - || (item.file_type == FileType::Symlink && item.symlink_dir_path.is_some()) - { - // symlink was resolved to directory already in the ItemInfo - PreviewType::Directory - } else { - check_preview_content_type(item) - } -} - -fn is_supported_ext(item: &ItemInfo) -> bool { - match &item.file_ext { - None => false, - Some(ext) => IMAGE_EXTENSION.contains(&ext.as_str()), - } -} |