mirror of
https://github.com/sharkdp/fd
synced 2024-11-02 07:51:38 +00:00
Use lscolors crate
Use my new [lscolors](https://github.com/sharkdp/lscolors) crate instead of the internal `lscolors` module - Speeds up `LS_COLORS` querying, leading to a nice 25% performance improvement when - Adds support for 24-bit colors and background colors closes #368 closes #363
This commit is contained in:
parent
afcdaf2e3d
commit
4b5efa3438
5 changed files with 19 additions and 279 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -87,6 +87,7 @@ dependencies = [
|
|||
"ignore 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lscolors 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -190,6 +191,14 @@ dependencies = [
|
|||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.0.1"
|
||||
|
@ -423,6 +432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
|
||||
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
|
||||
"checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2"
|
||||
"checksum lscolors 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3eea90e242149c448a1dac8ddb3ed75f49cba948a63482348b23a1b11ee0ac87"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
|
||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||
|
|
|
@ -41,6 +41,7 @@ regex = "1.0.0"
|
|||
regex-syntax = "0.6"
|
||||
ctrlc = "3.1"
|
||||
humantime = "1.1.1"
|
||||
lscolors = "0.1"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2.31.2"
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
// Copyright (c) 2017 fd developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use ansi_term::{Colour, Style};
|
||||
/// A parser for the `LS_COLORS` environment variable.
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Maps file extensions to ANSI colors / styles.
|
||||
pub type ExtensionStyles = HashMap<String, Style>;
|
||||
|
||||
/// Maps filenames to ANSI colors / styles.
|
||||
pub type FilenameStyles = HashMap<String, Style>;
|
||||
|
||||
const LS_CODES: &[&str] = &[
|
||||
"no", "no", "fi", "rs", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi", "so", "bd", "bd", "cd",
|
||||
"cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec", "su", "su", "sg", "sg", "st", "ow", "ow",
|
||||
"tw", "tw", "ca", "mh", "cl",
|
||||
];
|
||||
|
||||
/// Defines how different file system entries should be colorized / styled.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct LsColors {
|
||||
/// ANSI Style for directories.
|
||||
pub directory: Style,
|
||||
|
||||
/// ANSI style for symbolic links.
|
||||
pub symlink: Style,
|
||||
|
||||
/// ANSI style for executable files.
|
||||
pub executable: Style,
|
||||
|
||||
/// A map that defines ANSI styles for different file extensions.
|
||||
pub extensions: ExtensionStyles,
|
||||
|
||||
/// A map that defines ANSI styles for different specific filenames.
|
||||
pub filenames: FilenameStyles,
|
||||
}
|
||||
|
||||
impl Default for LsColors {
|
||||
/// Get a default LsColors structure.
|
||||
fn default() -> LsColors {
|
||||
LsColors {
|
||||
directory: Colour::Blue.bold(),
|
||||
symlink: Colour::Cyan.normal(),
|
||||
executable: Colour::Red.bold(),
|
||||
extensions: HashMap::new(),
|
||||
filenames: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LsColors {
|
||||
/// Parse a single text-decoration code (normal, bold, italic, ...).
|
||||
fn parse_decoration(code: &str) -> Option<fn(Colour) -> Style> {
|
||||
match code {
|
||||
"0" | "00" => Some(Colour::normal),
|
||||
"1" | "01" => Some(Colour::bold),
|
||||
"3" | "03" => Some(Colour::italic),
|
||||
"4" | "04" => Some(Colour::underline),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse ANSI escape sequences like `38;5;10;1`.
|
||||
fn parse_style(code: &str) -> Option<Style> {
|
||||
let mut split = code.split(';');
|
||||
|
||||
if let Some(first) = split.next() {
|
||||
// Try to match the first part as a text-decoration argument
|
||||
let mut decoration = LsColors::parse_decoration(first);
|
||||
|
||||
let c1 = if decoration.is_none() {
|
||||
Some(first)
|
||||
} else {
|
||||
split.next()
|
||||
};
|
||||
let c2 = split.next();
|
||||
let c3 = split.next();
|
||||
|
||||
let color = if c1 == Some("38") && c2 == Some("5") {
|
||||
let n_white = 7;
|
||||
let n = if let Some(num) = c3 {
|
||||
u8::from_str_radix(num, 10).unwrap_or(n_white)
|
||||
} else {
|
||||
n_white
|
||||
};
|
||||
|
||||
Colour::Fixed(n)
|
||||
} else if let Some(color_s) = c1 {
|
||||
match color_s {
|
||||
"30" => Colour::Black,
|
||||
"31" => Colour::Red,
|
||||
"32" => Colour::Green,
|
||||
"33" => Colour::Yellow,
|
||||
"34" => Colour::Blue,
|
||||
"35" => Colour::Purple,
|
||||
"36" => Colour::Cyan,
|
||||
_ => Colour::White,
|
||||
}
|
||||
} else {
|
||||
Colour::White
|
||||
};
|
||||
|
||||
if decoration.is_none() {
|
||||
// Try to find a decoration somewhere in the sequence
|
||||
decoration = code.split(';').flat_map(LsColors::parse_decoration).next();
|
||||
}
|
||||
|
||||
let ansi_style = decoration.unwrap_or(Colour::normal)(color);
|
||||
|
||||
Some(ansi_style)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new `LS_COLORS` entry.
|
||||
fn add_entry(&mut self, input: &str) {
|
||||
let mut parts = input.trim().split('=');
|
||||
if let Some(pattern) = parts.next() {
|
||||
if let Some(style_code) = parts.next() {
|
||||
// Ensure that the input was split into exactly two parts:
|
||||
if !parts.next().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(style) = LsColors::parse_style(style_code) {
|
||||
// Try to match against one of the known codes
|
||||
let res = LS_CODES.iter().find(|&&c| c == pattern);
|
||||
|
||||
if let Some(code) = res {
|
||||
match code.as_ref() {
|
||||
"di" => self.directory = style,
|
||||
"ln" => self.symlink = style,
|
||||
"ex" => self.executable = style,
|
||||
_ => return,
|
||||
}
|
||||
} else if pattern.starts_with("*.") {
|
||||
let extension = String::from(pattern).split_off(2);
|
||||
self.extensions.insert(extension, style);
|
||||
} else if pattern.starts_with('*') {
|
||||
let filename = String::from(pattern).split_off(1);
|
||||
self.filenames.insert(filename, style);
|
||||
} else {
|
||||
// Unknown/corrupt pattern
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a `LsColors` structure from a string.
|
||||
pub fn from_string(input: &str) -> LsColors {
|
||||
let mut lscolors = LsColors::default();
|
||||
|
||||
for s in input.split(':') {
|
||||
lscolors.add_entry(s);
|
||||
}
|
||||
|
||||
lscolors
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_simple() {
|
||||
assert_eq!(Some(Colour::Red.normal()), LsColors::parse_style("31"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_decoration() {
|
||||
assert_eq!(Some(Colour::Red.normal()), LsColors::parse_style("00;31"));
|
||||
|
||||
assert_eq!(Some(Colour::Blue.italic()), LsColors::parse_style("03;34"));
|
||||
|
||||
assert_eq!(Some(Colour::Cyan.bold()), LsColors::parse_style("01;36"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_decoration_backwards() {
|
||||
assert_eq!(Some(Colour::Blue.italic()), LsColors::parse_style("34;03"));
|
||||
|
||||
assert_eq!(Some(Colour::Cyan.bold()), LsColors::parse_style("36;01"));
|
||||
|
||||
assert_eq!(Some(Colour::Red.normal()), LsColors::parse_style("31;00"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_256() {
|
||||
assert_eq!(
|
||||
Some(Colour::Fixed(115).normal()),
|
||||
LsColors::parse_style("38;5;115")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Some(Colour::Fixed(115).normal()),
|
||||
LsColors::parse_style("00;38;5;115")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Some(Colour::Fixed(119).bold()),
|
||||
LsColors::parse_style("01;38;5;119")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Some(Colour::Fixed(119).bold()),
|
||||
LsColors::parse_style("38;5;119;01")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_string() {
|
||||
assert_eq!(LsColors::default(), LsColors::from_string(&String::new()));
|
||||
|
||||
let result = LsColors::from_string(&String::from(
|
||||
"rs=0:di=03;34:ln=01;36:*.foo=01;35:*README=33",
|
||||
));
|
||||
|
||||
assert_eq!(Colour::Blue.italic(), result.directory);
|
||||
assert_eq!(Colour::Cyan.bold(), result.symlink);
|
||||
assert_eq!(Some(&Colour::Purple.bold()), result.extensions.get("foo"));
|
||||
assert_eq!(
|
||||
Some(&Colour::Yellow.normal()),
|
||||
result.filenames.get("README")
|
||||
);
|
||||
}
|
||||
}
|
11
src/main.rs
11
src/main.rs
|
@ -16,6 +16,7 @@ extern crate lazy_static;
|
|||
extern crate humantime;
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
extern crate libc;
|
||||
extern crate lscolors;
|
||||
extern crate num_cpus;
|
||||
extern crate regex;
|
||||
extern crate regex_syntax;
|
||||
|
@ -27,7 +28,6 @@ mod app;
|
|||
mod exec;
|
||||
mod exit_codes;
|
||||
pub mod fshelper;
|
||||
pub mod lscolors;
|
||||
mod output;
|
||||
mod walk;
|
||||
|
||||
|
@ -38,6 +38,7 @@ use std::sync::Arc;
|
|||
use std::time;
|
||||
|
||||
use atty::Stream;
|
||||
use lscolors::LsColors;
|
||||
use regex::{RegexBuilder, RegexSetBuilder};
|
||||
|
||||
use exec::CommandTemplate;
|
||||
|
@ -46,7 +47,6 @@ use internal::{
|
|||
opts::FdOptions,
|
||||
pattern_has_uppercase_char, transform_args_with_exec, FileTypes,
|
||||
};
|
||||
use lscolors::LsColors;
|
||||
|
||||
fn main() {
|
||||
let checked_args = transform_args_with_exec(env::args_os());
|
||||
|
@ -132,12 +132,7 @@ fn main() {
|
|||
let colored_output = colored_output && ansi_term::enable_ansi_support().is_ok();
|
||||
|
||||
let ls_colors = if colored_output {
|
||||
Some(
|
||||
env::var("LS_COLORS")
|
||||
.ok()
|
||||
.map(|val| LsColors::from_string(&val))
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
Some(LsColors::from_env().unwrap_or_default())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
// according to those terms.
|
||||
|
||||
use exit_codes::ExitCode;
|
||||
use fshelper::is_executable;
|
||||
use internal::opts::FdOptions;
|
||||
use lscolors::LsColors;
|
||||
use lscolors::{LsColors, Style};
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops::Deref;
|
||||
|
@ -71,7 +70,10 @@ fn print_entry_colorized(
|
|||
let comp_str = component.as_os_str().to_string_lossy();
|
||||
component_path.push(Path::new(comp_str.deref()));
|
||||
|
||||
let style = get_path_style(&component_path, ls_colors).unwrap_or(&default_style);
|
||||
let style = ls_colors.style_for_path(&component_path);
|
||||
let style = style
|
||||
.map(Style::to_ansi_term_style)
|
||||
.unwrap_or(default_style);
|
||||
|
||||
write!(handle, "{}{}", separator, style.paint(comp_str))?;
|
||||
|
||||
|
@ -104,35 +106,3 @@ fn print_entry_uncolorized(path: &Path, config: &FdOptions) -> io::Result<()> {
|
|||
let path_str = path.to_string_lossy();
|
||||
write!(&mut io::stdout(), "{}{}", path_str, separator)
|
||||
}
|
||||
|
||||
fn get_path_style<'a>(path: &Path, ls_colors: &'a LsColors) -> Option<&'a ansi_term::Style> {
|
||||
if path
|
||||
.symlink_metadata()
|
||||
.map(|md| md.file_type().is_symlink())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Some(&ls_colors.symlink);
|
||||
}
|
||||
|
||||
let metadata = path.metadata();
|
||||
|
||||
if metadata.as_ref().map(|md| md.is_dir()).unwrap_or(false) {
|
||||
Some(&ls_colors.directory)
|
||||
} else if metadata.map(|md| is_executable(&md)).unwrap_or(false) {
|
||||
Some(&ls_colors.executable)
|
||||
} else if let Some(filename_style) = path
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.and_then(|n| ls_colors.filenames.get(n))
|
||||
{
|
||||
Some(filename_style)
|
||||
} else if let Some(extension_style) = path
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.and_then(|e| ls_colors.extensions.get(e))
|
||||
{
|
||||
Some(extension_style)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue