From a26bd3232cc1d15a942ece4172320ff3cfdfd0fb Mon Sep 17 00:00:00 2001 From: Jonathan Goren Date: Mon, 15 Nov 2021 09:57:40 +0200 Subject: [PATCH] append trailing slash to folders update changelog --- CHANGELOG.md | 1 + src/output.rs | 74 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47bfb05..ec1aeef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ ## Changes +- Directories are now printed with an additional path separator at the end: `foo/bar/` - Apply custom `--path-separator` to commands run with `--exec(-batch)` and `--list-details`, see #697 (@aswild) ## Other diff --git a/src/output.rs b/src/output.rs index 37ed357..6accf30 100644 --- a/src/output.rs +++ b/src/output.rs @@ -3,6 +3,7 @@ use std::io::{self, Write}; use std::path::Path; use lscolors::{Indicator, LsColors, Style}; +use once_cell::sync::Lazy; use crate::config::Config; use crate::entry::DirEntry; @@ -10,23 +11,27 @@ use crate::error::print_error; use crate::exit_codes::ExitCode; use crate::filesystem::strip_current_dir; +static MAIN_SEPARATOR_STR: Lazy = Lazy::new(|| std::path::MAIN_SEPARATOR.to_string()); + fn replace_path_separator(path: &str, new_path_separator: &str) -> String { path.replace(std::path::MAIN_SEPARATOR, new_path_separator) } -// TODO: this function is performance critical and can probably be optimized -pub fn print_entry(stdout: &mut W, entry: &DirEntry, config: &Config) { +fn stripped_path<'a>(entry: &'a DirEntry, config: &Config) -> &'a Path { let path = entry.path(); - let path = if config.strip_cwd_prefix { + if config.strip_cwd_prefix { strip_current_dir(path) } else { path - }; + } +} +// TODO: this function is performance critical and can probably be optimized +pub fn print_entry(stdout: &mut W, entry: &DirEntry, config: &Config) { let r = if let Some(ref ls_colors) = config.ls_colors { - print_entry_colorized(stdout, path, config, ls_colors) + print_entry_colorized(stdout, entry, config, ls_colors) } else { - print_entry_uncolorized(stdout, path, config) + print_entry_uncolorized(stdout, entry, config) }; if let Err(e) = r { @@ -40,15 +45,43 @@ pub fn print_entry(stdout: &mut W, entry: &DirEntry, config: &Config) } } +// Display a trailing slash if the path is a directory and the config option is enabled. +// If the path_separator option is set, display that instead. +// The trailing slash will not be colored. +#[inline] +fn print_trailing_slash( + stdout: &mut W, + entry: &DirEntry, + config: &Config, + style: Option<&Style>, +) -> io::Result<()> { + if entry.file_type().map_or(false, |ft| ft.is_dir()) { + let separator = config + .path_separator + .as_ref() + .unwrap_or(&MAIN_SEPARATOR_STR); + write!( + stdout, + "{}", + style + .map(Style::to_ansi_term_style) + .unwrap_or_default() + .paint(separator) + )?; + } + Ok(()) +} + // TODO: this function is performance critical and can probably be optimized fn print_entry_colorized( stdout: &mut W, - path: &Path, + entry: &DirEntry, config: &Config, ls_colors: &LsColors, ) -> io::Result<()> { // Split the path between the parent and the last component let mut offset = 0; + let path = stripped_path(entry, config); let path_str = path.to_string_lossy(); if let Some(parent) = path.parent() { @@ -76,11 +109,18 @@ fn print_entry_colorized( } let style = ls_colors - .style_for_path(path) + .style_for_path_with_metadata(path, entry.metadata()) .map(Style::to_ansi_term_style) .unwrap_or_default(); write!(stdout, "{}", style.paint(&path_str[offset..]))?; + print_trailing_slash( + stdout, + entry, + config, + ls_colors.style_for_indicator(Indicator::Directory), + )?; + if config.null_separator { write!(stdout, "\0")?; } else { @@ -93,42 +133,46 @@ fn print_entry_colorized( // TODO: this function is performance critical and can probably be optimized fn print_entry_uncolorized_base( stdout: &mut W, - path: &Path, + entry: &DirEntry, config: &Config, ) -> io::Result<()> { let separator = if config.null_separator { "\0" } else { "\n" }; + let path = stripped_path(entry, config); let mut path_string = path.to_string_lossy(); if let Some(ref separator) = config.path_separator { *path_string.to_mut() = replace_path_separator(&path_string, separator); } - write!(stdout, "{}{}", path_string, separator) + write!(stdout, "{}", path_string)?; + print_trailing_slash(stdout, entry, config, None)?; + write!(stdout, "{}", separator) } #[cfg(not(unix))] fn print_entry_uncolorized( stdout: &mut W, - path: &Path, + entry: &DirEntry, config: &Config, ) -> io::Result<()> { - print_entry_uncolorized_base(stdout, path, config) + print_entry_uncolorized_base(stdout, entry, config) } #[cfg(unix)] fn print_entry_uncolorized( stdout: &mut W, - path: &Path, + entry: &DirEntry, config: &Config, ) -> io::Result<()> { use std::os::unix::ffi::OsStrExt; if config.interactive_terminal || config.path_separator.is_some() { // Fall back to the base implementation - print_entry_uncolorized_base(stdout, path, config) + print_entry_uncolorized_base(stdout, entry, config) } else { // Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes let separator = if config.null_separator { b"\0" } else { b"\n" }; - stdout.write_all(path.as_os_str().as_bytes())?; + stdout.write_all(stripped_path(entry, config).as_os_str().as_bytes())?; + print_trailing_slash(stdout, entry, config, None)?; stdout.write_all(separator) } }