Skip to content

Commit

Permalink
Don't color each path component separately
Browse files Browse the repository at this point in the history
It can be expensive to color each path component separately, requiring a
stat() call on each component.  For deep hierarchies this can result in
quadratic overhead.  Instead, just color the path up to the basename as
a directory.

Fixes sharkdp#720.
  • Loading branch information
tavianator committed Sep 16, 2021
1 parent 881735e commit bf8636d
Showing 1 changed file with 30 additions and 10 deletions.
40 changes: 30 additions & 10 deletions src/output.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::borrow::Cow;
use std::io::{self, StdoutLock, Write};
use std::path::Path;
use std::process;

use lscolors::{LsColors, Style};
use lscolors::{Indicator, LsColors, Style};

use crate::error::print_error;
use crate::exit_codes::ExitCode;
Expand Down Expand Up @@ -49,21 +50,40 @@ fn print_entry_colorized(
config: &Options,
ls_colors: &LsColors,
) -> io::Result<()> {
let default_style = ansi_term::Style::default();
// Split the path between the parent and the last component
let mut offset = 0;
let path_str = path.to_string_lossy();

// Traverse the path and colorize each component
for (component, style) in ls_colors.style_for_path_components(path) {
let style = style
.map(Style::to_ansi_term_style)
.unwrap_or(default_style);
if let Some(parent) = path.parent() {
offset = parent.to_string_lossy().len();
for c in path_str[offset..].chars() {
if std::path::is_separator(c) {
offset += c.len_utf8();
} else {
break;
}
}
}

let mut path_string = component.to_string_lossy();
if offset > 0 {
let mut parent_str = Cow::from(&path_str[..offset]);
if let Some(ref separator) = config.path_separator {
*path_string.to_mut() = replace_path_separator(&path_string, separator);
*parent_str.to_mut() = replace_path_separator(&parent_str, separator);
}
write!(stdout, "{}", style.paint(path_string))?;

let style = ls_colors
.style_for_indicator(Indicator::Directory)
.map(Style::to_ansi_term_style)
.unwrap_or_default();
write!(stdout, "{}", style.paint(parent_str))?;
}

let style = ls_colors
.style_for_path(path)
.map(Style::to_ansi_term_style)
.unwrap_or_default();
write!(stdout, "{}", style.paint(&path_str[offset..]))?;

if config.null_separator {
write!(stdout, "\0")
} else {
Expand Down

0 comments on commit bf8636d

Please sign in to comment.