Move ls back to last-known-good state (#6175)

* revert the recent ls changes

* cargo fmt
This commit is contained in:
JT 2022-07-29 11:00:54 +12:00 committed by GitHub
parent 10e463180e
commit 98e199f7b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 134 additions and 312 deletions

View file

@ -1,17 +1,15 @@
use crate::DirBuilder; use crate::DirBuilder;
use crate::DirInfo; use crate::DirInfo;
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc}; use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
use itertools::Itertools;
use nu_engine::env::current_dir; use nu_engine::env::current_dir;
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_glob::MatchOptions; use nu_glob::MatchOptions;
use nu_path::expand_to_real_path; use nu_path::expand_to_real_path;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::IntoPipelineData;
use nu_protocol::{ use nu_protocol::{
Category, DataSource, Example, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata, Category, DataSource, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Value, PipelineMetadata, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
}; };
use pathdiff::diff_paths; use pathdiff::diff_paths;
@ -40,11 +38,7 @@ impl Command for Ls {
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
Signature::build("ls") Signature::build("ls")
// Using a string instead of a glob pattern shape so it won't auto-expand // Using a string instead of a glob pattern shape so it won't auto-expand
.rest( .optional("pattern", SyntaxShape::String, "the glob pattern to use")
"pattern(s)",
SyntaxShape::String,
"the glob pattern(s) to use",
)
.switch("all", "Show hidden files", Some('a')) .switch("all", "Show hidden files", Some('a'))
.switch( .switch(
"long", "long",
@ -87,23 +81,19 @@ impl Command for Ls {
let call_span = call.head; let call_span = call.head;
let cwd = current_dir(engine_state, stack)?; let cwd = current_dir(engine_state, stack)?;
let mut shell_errors: Vec<ShellError> = vec![]; let pattern_arg: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
let pattern_args: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
let glob_results = if !pattern_args.is_empty() {
pattern_args
.into_iter()
.flat_map(|pattern_arg| {
let mut path = expand_to_real_path(pattern_arg.clone().item);
let p_tag = pattern_arg.span;
let cwd = cwd.clone();
let ctrl_c = ctrl_c.clone();
let expanded = nu_path::expand_path_with(&path, &cwd); let (path, p_tag, absolute_path) = match pattern_arg {
// Avoid checking and pushing "*" to the path when directory (do not show contents) flag is true Some(p) => {
if !directory && expanded.is_dir() { let p_tag = p.span;
if permission_denied(&path) { let mut p = expand_to_real_path(p.item);
#[cfg(unix)]
let error_msg = format!( let expanded = nu_path::expand_path_with(&p, &cwd);
// Avoid checking and pushing "*" to the path when directory (do not show contents) flag is true
if !directory && expanded.is_dir() {
if permission_denied(&p) {
#[cfg(unix)]
let error_msg = format!(
"The permissions of {:o} do not allow access for this user", "The permissions of {:o} do not allow access for this user",
expanded expanded
.metadata() .metadata()
@ -114,225 +104,94 @@ impl Command for Ls {
.mode() .mode()
& 0o0777 & 0o0777
); );
#[cfg(not(unix))] #[cfg(not(unix))]
let error_msg = String::from("Permission denied"); let error_msg = String::from("Permission denied");
shell_errors.push(ShellError::GenericError( return Err(ShellError::GenericError(
"Permission denied".to_string(), "Permission denied".to_string(),
error_msg, error_msg,
Some(p_tag), Some(p_tag),
None,
Vec::new(),
));
}
if is_empty_dir(&expanded) {
return Vec::from([Value::nothing(call_span)]).into_iter();
}
path.push("*");
}
let absolute_path = path.is_absolute();
let hidden_dir_specified = is_hidden_dir(&path);
let glob_path = Spanned {
item: path.display().to_string(),
span: p_tag,
};
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) =
match nu_engine::glob_from(&glob_path, &cwd, call_span, glob_options) {
Ok((prefix, paths)) => (prefix, paths),
Err(e) => {
shell_errors.push(e);
return Vec::from([Value::nothing(call_span)]).into_iter();
}
};
let mut paths_peek = paths.peekable();
if paths_peek.peek().is_none() {
shell_errors.push(ShellError::GenericError(
format!("No matches found for {}", &path.display().to_string()),
"".to_string(),
None, None,
Some("no matches found".to_string()),
Vec::new(), Vec::new(),
)); ));
} }
if is_empty_dir(&expanded) {
let mut hidden_dirs = vec![]; return Ok(Value::nothing(call_span).into_pipeline_data());
}
paths_peek p.push("*");
.into_iter() }
.filter_map(move |x| match x { let absolute_path = p.is_absolute();
Ok(path) => { (p, p_tag, absolute_path)
let metadata = match std::fs::symlink_metadata(&path) {
Ok(metadata) => Some(metadata),
Err(_) => None,
};
if path_contains_hidden_folder(&path, &hidden_dirs) {
return None;
}
if !all && !hidden_dir_specified && is_hidden_dir(&path) {
if path.is_dir() {
hidden_dirs.push(path);
}
return None;
}
let display_name = if short_names {
path.file_name().map(|os| os.to_string_lossy().to_string())
} else if full_paths || absolute_path {
Some(path.to_string_lossy().to_string())
} else if let Some(prefix) = &prefix {
if let Ok(remainder) = path.strip_prefix(&prefix) {
if directory {
// When the path is the same as the cwd, path_diff should be "."
let path_diff = if let Some(path_diff_not_dot) =
diff_paths(&path, &cwd)
{
let path_diff_not_dot =
path_diff_not_dot.to_string_lossy();
if path_diff_not_dot.is_empty() {
".".to_string()
} else {
path_diff_not_dot.to_string()
}
} else {
path.to_string_lossy().to_string()
};
Some(path_diff)
} else {
let new_prefix =
if let Some(pfx) = diff_paths(&prefix, &cwd) {
pfx
} else {
prefix.to_path_buf()
};
Some(
new_prefix
.join(remainder)
.to_string_lossy()
.to_string(),
)
}
} else {
Some(path.to_string_lossy().to_string())
}
} else {
Some(path.to_string_lossy().to_string())
}
.ok_or_else(|| {
ShellError::GenericError(
format!("Invalid file name: {:}", path.to_string_lossy()),
"invalid file name".into(),
Some(call_span),
None,
Vec::new(),
)
});
match display_name {
Ok(name) => {
let entry = dir_entry_dict(
&path,
&name,
metadata.as_ref(),
call_span,
long,
du,
ctrl_c.clone(),
);
match entry {
Ok(value) => Some(value),
Err(err) => Some(Value::Error { error: err }),
}
}
Err(err) => Some(Value::Error { error: err }),
}
}
_ => Some(Value::Nothing { span: call_span }),
})
.collect_vec()
.into_iter()
})
.collect_vec()
} else {
let (path, p_tag, absolute_path) = if directory {
(PathBuf::from("."), call_span, false)
} else if is_empty_dir(current_dir(engine_state, stack)?) {
return Ok(Value::nothing(call_span).into_pipeline_data());
} else {
(PathBuf::from("./*"), call_span, false)
};
let hidden_dir_specified = is_hidden_dir(&path);
let glob_path = Spanned {
item: path.display().to_string(),
span: p_tag,
};
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() {
return Err(ShellError::GenericError(
format!("No matches found for {}", &path.display().to_string()),
"".to_string(),
None,
Some("no matches found".to_string()),
Vec::new(),
));
} }
None => {
// Avoid pushing "*" to the default path when directory (do not show contents) flag is true
if directory {
(PathBuf::from("."), call_span, false)
} else if is_empty_dir(current_dir(engine_state, stack)?) {
return Ok(Value::nothing(call_span).into_pipeline_data());
} else {
(PathBuf::from("./*"), call_span, false)
}
}
};
let mut hidden_dirs = vec![]; let hidden_dir_specified = is_hidden_dir(&path);
paths_peek let glob_path = Spanned {
.into_iter() item: path.display().to_string(),
.filter_map(move |x| match x { span: p_tag,
Ok(path) => { };
let metadata = match std::fs::symlink_metadata(&path) {
Ok(metadata) => Some(metadata), let glob_options = if all {
Err(_) => None, None
}; } else {
if path_contains_hidden_folder(&path, &hidden_dirs) { let mut glob_options = MatchOptions::new();
return None; 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() {
return Err(ShellError::GenericError(
format!("No matches found for {}", &path.display().to_string()),
"".to_string(),
None,
Some("no matches found".to_string()),
Vec::new(),
));
}
let mut hidden_dirs = vec![];
Ok(paths_peek
.into_iter()
.filter_map(move |x| match x {
Ok(path) => {
let metadata = match std::fs::symlink_metadata(&path) {
Ok(metadata) => Some(metadata),
Err(_) => None,
};
if path_contains_hidden_folder(&path, &hidden_dirs) {
return None;
}
if !all && !hidden_dir_specified && is_hidden_dir(&path) {
if path.is_dir() {
hidden_dirs.push(path);
} }
return None;
}
if !all && !hidden_dir_specified && is_hidden_dir(&path) { let display_name = if short_names {
if path.is_dir() { path.file_name().map(|os| os.to_string_lossy().to_string())
hidden_dirs.push(path); } else if full_paths || absolute_path {
} Some(path.to_string_lossy().to_string())
return None; } else if let Some(prefix) = &prefix {
} if let Ok(remainder) = path.strip_prefix(&prefix) {
if directory {
let display_name = if short_names { // When the path is the same as the cwd, path_diff should be "."
path.file_name().map(|os| os.to_string_lossy().to_string()) let path_diff =
} else if full_paths || absolute_path { if let Some(path_diff_not_dot) = diff_paths(&path, &cwd) {
Some(path.to_string_lossy().to_string())
} else if let Some(prefix) = &prefix {
if let Ok(remainder) = path.strip_prefix(&prefix) {
if directory {
// When the path is the same as the cwd, path_diff should be "."
let path_diff = if let Some(path_diff_not_dot) =
diff_paths(&path, &cwd)
{
let path_diff_not_dot = path_diff_not_dot.to_string_lossy(); let path_diff_not_dot = path_diff_not_dot.to_string_lossy();
if path_diff_not_dot.is_empty() { if path_diff_not_dot.is_empty() {
".".to_string() ".".to_string()
@ -343,63 +202,53 @@ impl Command for Ls {
path.to_string_lossy().to_string() path.to_string_lossy().to_string()
}; };
Some(path_diff) Some(path_diff)
} else {
let new_prefix = if let Some(pfx) = diff_paths(&prefix, &cwd) {
pfx
} else {
prefix.to_path_buf()
};
Some(new_prefix.join(remainder).to_string_lossy().to_string())
}
} else { } else {
Some(path.to_string_lossy().to_string()) let new_prefix = if let Some(pfx) = diff_paths(&prefix, &cwd) {
pfx
} else {
prefix.to_path_buf()
};
Some(new_prefix.join(remainder).to_string_lossy().to_string())
} }
} else { } else {
Some(path.to_string_lossy().to_string()) Some(path.to_string_lossy().to_string())
} }
.ok_or_else(|| { } else {
ShellError::GenericError( Some(path.to_string_lossy().to_string())
format!("Invalid file name: {:}", path.to_string_lossy()),
"invalid file name".into(),
Some(call_span),
None,
Vec::new(),
)
});
match display_name {
Ok(name) => {
let entry = dir_entry_dict(
&path,
&name,
metadata.as_ref(),
call_span,
long,
du,
ctrl_c.clone(),
);
match entry {
Ok(value) => Some(value),
Err(err) => Some(Value::Error { error: err }),
}
}
Err(err) => Some(Value::Error { error: err }),
}
} }
_ => Some(Value::Nothing { span: call_span }), .ok_or_else(|| {
}) ShellError::GenericError(
.collect_vec() format!("Invalid file name: {:}", path.to_string_lossy()),
}; "invalid file name".into(),
Some(call_span),
None,
Vec::new(),
)
});
if !shell_errors.is_empty() { match display_name {
return Err(shell_errors.pop().expect("Vec pop error")); Ok(name) => {
} let entry = dir_entry_dict(
&path,
Ok(glob_results &name,
.into_iter() metadata.as_ref(),
.filter(|result| !matches!(result, Value::Nothing { .. })) call_span,
long,
du,
ctrl_c.clone(),
);
match entry {
Ok(value) => Some(value),
Err(err) => Some(Value::Error { error: err }),
}
}
Err(err) => Some(Value::Error { error: err }),
}
}
_ => Some(Value::Nothing { span: call_span }),
})
.into_pipeline_data_with_metadata( .into_pipeline_data_with_metadata(
PipelineMetadata { PipelineMetadata {
data_source: DataSource::Ls, data_source: DataSource::Ls,
@ -430,11 +279,6 @@ impl Command for Ls {
example: "ls *.rs", example: "ls *.rs",
result: None, result: None,
}, },
Example {
description: "List all rust files and all toml files",
example: "ls *.rs *.toml",
result: None,
},
Example { Example {
description: "List all files and directories whose name do not contain 'bar'", description: "List all files and directories whose name do not contain 'bar'",
example: "ls -s | where name !~ bar", example: "ls -s | where name !~ bar",

View file

@ -335,28 +335,6 @@ fn lists_files_including_starting_with_dot() {
}) })
} }
#[test]
fn lists_regular_files_using_multiple_asterisk_wildcards() {
Playground::setup("ls_test_10", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls *.txt *.clu
| length
"#
));
assert_eq!(actual.out, "4");
})
}
#[test] #[test]
fn list_all_columns() { fn list_all_columns() {
Playground::setup("ls_test_all_columns", |dirs, sandbox| { Playground::setup("ls_test_all_columns", |dirs, sandbox| {