make ls works better with glob (#5691)

* fix glob behavior

* fix doc
This commit is contained in:
WindSoilder 2022-05-31 08:13:27 +08:00 committed by GitHub
parent f5519e2a09
commit 0769e9b750
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 4 deletions

View File

@ -19,6 +19,7 @@ const GLOB_PARAMS: nu_glob::MatchOptions = nu_glob::MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
#[derive(Clone)]

View File

@ -3,6 +3,7 @@ use crate::DirInfo;
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
use nu_engine::env::current_dir;
use nu_engine::CallExt;
use nu_glob::MatchOptions;
use nu_path::expand_to_real_path;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
@ -128,7 +129,15 @@ impl Command for Ls {
item: path.display().to_string(),
span: p_tag,
};
let (prefix, paths) = nu_engine::glob_from(&glob_path, &cwd, call_span)?;
let glob_options = if all {
None
} else {
let mut glob_options = MatchOptions::new();
glob_options.recursive_match_hidden_dir = false;
Some(glob_options)
};
let (prefix, paths) = nu_engine::glob_from(&glob_path, &cwd, call_span, glob_options)?;
let mut paths_peek = paths.peekable();
if paths_peek.peek().is_none() {

View File

@ -14,6 +14,7 @@ const GLOB_PARAMS: nu_glob::MatchOptions = nu_glob::MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
#[derive(Clone)]

View File

@ -24,6 +24,7 @@ const GLOB_PARAMS: nu_glob::MatchOptions = nu_glob::MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
#[derive(Clone)]

View File

@ -14,6 +14,7 @@ const GLOB_PARAMS: MatchOptions = MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
#[derive(Clone)]

View File

@ -417,7 +417,9 @@ impl ExternalCommand {
let cwd = PathBuf::from(cwd);
if arg.item.contains('*') {
if let Ok((prefix, matches)) = nu_engine::glob_from(&arg, &cwd, self.name.span) {
if let Ok((prefix, matches)) =
nu_engine::glob_from(&arg, &cwd, self.name.span, None)
{
let matches: Vec<_> = matches.collect();
// FIXME: do we want to special-case this further? We might accidentally expand when they don't

View File

@ -244,6 +244,42 @@ fn lists_all_hidden_files_when_glob_does_not_contain_dot() {
})
}
#[test]
// TODO Remove this cfg value when we have an OS-agnostic way
// of creating hidden files using the playground.
#[cfg(unix)]
fn glob_with_hidden_directory() {
Playground::setup("ls_test_8", |dirs, sandbox| {
sandbox.within(".dir_b").with_files(vec![
EmptyFile("andres.10.txt"),
EmptyFile("chicken_not_to_be_picked_up.100.txt"),
EmptyFile(".dotfile3"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls **/*
| length
"#
));
assert_eq!(actual.out, "");
assert!(actual.err.contains("No matches found"));
// will list files if provide `-a` flag.
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls -a **/*
| length
"#
));
assert_eq!(actual.out, "4");
})
}
#[test]
#[cfg(unix)]
fn fails_with_ls_to_dir_without_permission() {

View File

@ -2,6 +2,7 @@
use std::os::unix::fs::PermissionsExt;
use std::path::{Component, Path, PathBuf};
use nu_glob::MatchOptions;
use nu_path::{canonicalize_with, expand_path_with};
use nu_protocol::{ShellError, Span, Spanned};
@ -17,6 +18,7 @@ pub fn glob_from(
pattern: &Spanned<String>,
cwd: &Path,
span: Span,
options: Option<MatchOptions>,
) -> Result<
(
Option<PathBuf>,
@ -82,8 +84,9 @@ pub fn glob_from(
};
let pattern = pattern.to_string_lossy().to_string();
let glob_options = options.unwrap_or_else(MatchOptions::new);
let glob = nu_glob::glob(&pattern).map_err(|err| {
let glob = nu_glob::glob_with(&pattern, glob_options).map_err(|err| {
nu_protocol::ShellError::GenericError(
"Error extracting glob pattern".into(),
err.to_string(),

View File

@ -49,6 +49,7 @@
//! case_sensitive: false,
//! require_literal_separator: false,
//! require_literal_leading_dot: false,
//! recursive_match_hidden_dir: true,
//! };
//! for entry in glob_with("local/*a*", options).unwrap() {
//! if let Ok(path) = entry {
@ -376,7 +377,16 @@ impl Iterator for Paths {
}
if is_dir(&path) {
// the path is a directory, so it's a match
// the path is a directory, check if matched according
// to `hidden_dir_recursive` option.
if !self.options.recursive_match_hidden_dir
&& path
.file_name()
.map(|name| name.to_string_lossy().starts_with('.'))
.unwrap_or(false)
{
continue;
}
// push this directory's contents
fill_todo(
@ -872,6 +882,10 @@ pub struct MatchOptions {
/// conventionally considered hidden on Unix systems and it might be
/// desirable to skip them when listing files.
pub require_literal_leading_dot: bool,
/// if given pattern contains `**`, this flag check if `**` matches hidden directory.
/// For example: if true, `**` will match `.abcdef/ghi`.
pub recursive_match_hidden_dir: bool,
}
impl MatchOptions {
@ -886,6 +900,7 @@ impl MatchOptions {
/// case_sensitive: true,
/// require_literal_separator: false,
/// require_literal_leading_dot: false
/// recursive_match_hidden_dir: true,
/// }
/// ```
pub fn new() -> Self {
@ -893,6 +908,7 @@ impl MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
}
}
}
@ -1084,6 +1100,7 @@ mod test {
case_sensitive: false,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
assert!(pat.matches_with("aBcDeFg", options));
@ -1098,11 +1115,13 @@ mod test {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
let options_not_require_literal = MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
assert!(Pattern::new("abc/def")
@ -1132,11 +1151,13 @@ mod test {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: true,
recursive_match_hidden_dir: true,
};
let options_not_require_literal_leading_dot = MatchOptions {
case_sensitive: true,
require_literal_separator: false,
require_literal_leading_dot: false,
recursive_match_hidden_dir: true,
};
let f = |options| {