ls: align --ignore behavior with that of GNU ls

This commit is contained in:
Ackerley Tng 2022-07-31 09:46:11 -07:00
parent 0f98bd01e1
commit d84803b72f
2 changed files with 83 additions and 13 deletions

View file

@ -14,7 +14,7 @@ use clap::{
builder::{NonEmptyStringValueParser, ValueParser}, builder::{NonEmptyStringValueParser, ValueParser},
crate_version, Arg, Command, crate_version, Arg, Command,
}; };
use glob::Pattern; use glob::{MatchOptions, Pattern};
use lscolors::LsColors; use lscolors::LsColors;
use number_prefix::NumberPrefix; use number_prefix::NumberPrefix;
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
@ -41,6 +41,7 @@ use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
#[cfg(unix)] #[cfg(unix)]
use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
use uucore::parse_glob;
use uucore::quoting_style::{escape_name, QuotingStyle}; use uucore::quoting_style::{escape_name, QuotingStyle};
use uucore::{ use uucore::{
display::Quotable, display::Quotable,
@ -765,7 +766,7 @@ impl Config {
.into_iter() .into_iter()
.flatten() .flatten()
{ {
match Pattern::new(pattern) { match parse_glob::from_str(pattern) {
Ok(p) => { Ok(p) => {
ignore_patterns.push(p); ignore_patterns.push(p);
} }
@ -779,7 +780,7 @@ impl Config {
.into_iter() .into_iter()
.flatten() .flatten()
{ {
match Pattern::new(pattern) { match parse_glob::from_str(pattern) {
Ok(p) => { Ok(p) => {
ignore_patterns.push(p); ignore_patterns.push(p);
} }
@ -1877,16 +1878,18 @@ fn should_display(entry: &DirEntry, config: &Config) -> bool {
return false; return false;
} }
// check if explicitly ignored // check if it is among ignore_patterns
for pattern in &config.ignore_patterns { let options = MatchOptions {
if pattern.matches(entry.file_name().to_str().unwrap()) { // setting require_literal_leading_dot to match behavior in GNU ls
return false; require_literal_leading_dot: true,
}; require_literal_separator: false,
continue; case_sensitive: true,
} };
let file_name = entry.file_name().into_string().unwrap();
// else default to display !config
true .ignore_patterns
.iter()
.any(|p| p.matches_with(&file_name, options))
} }
fn enter_directory( fn enter_directory(

View file

@ -2681,6 +2681,73 @@ fn test_ls_ignore_backups() {
.stdout_does_not_contain(".somehiddenbackup~"); .stdout_does_not_contain(".somehiddenbackup~");
} }
// This test fails on windows, see details at #3985
#[cfg(not(windows))]
#[test]
fn test_ls_ignore_explicit_period() {
// In ls ignore patterns, leading periods must be explicitly specified
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch(".hidden.yml");
at.touch("regular.yml");
scene
.ucmd()
.arg("-a")
.arg("--ignore")
.arg("?hidden.yml")
.succeeds()
.stdout_contains(".hidden.yml")
.stdout_contains("regular.yml");
scene
.ucmd()
.arg("-a")
.arg("--ignore")
.arg("*.yml")
.succeeds()
.stdout_contains(".hidden.yml")
.stdout_does_not_contain("regular.yml");
// Leading period is explicitly specified
scene
.ucmd()
.arg("-a")
.arg("--ignore")
.arg(".*.yml")
.succeeds()
.stdout_does_not_contain(".hidden.yml")
.stdout_contains("regular.yml");
}
// This test fails on windows, see details at #3985
#[cfg(not(windows))]
#[test]
fn test_ls_ignore_negation() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("apple");
at.touch("boy");
scene
.ucmd()
.arg("--ignore")
.arg("[!a]*")
.succeeds()
.stdout_contains("apple")
.stdout_does_not_contain("boy");
scene
.ucmd()
.arg("--ignore")
.arg("[^a]*")
.succeeds()
.stdout_contains("apple")
.stdout_does_not_contain("boy");
}
#[test] #[test]
fn test_ls_directory() { fn test_ls_directory() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());