mirror of
https://github.com/nushell/nushell.git
synced 2025-05-08 03:54:27 +02:00
check signals in nu-glob
and ls
(#15140)
Fixes #10144 # User-Facing Changes Long running glob expansions and `ls` runs (e.g. `ls /**/*`) can now be interrupted with ctrl-c.
This commit is contained in:
parent
48bdcc71f4
commit
c5a14bb8ff
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3784,6 +3784,7 @@ name = "nu-glob"
|
|||||||
version = "0.102.1"
|
version = "0.102.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
|
"nu-protocol",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -118,7 +118,7 @@ impl Command for Du {
|
|||||||
min_size,
|
min_size,
|
||||||
};
|
};
|
||||||
Ok(
|
Ok(
|
||||||
du_for_one_pattern(args, ¤t_dir, tag, engine_state.signals())?
|
du_for_one_pattern(args, ¤t_dir, tag, engine_state.signals().clone())?
|
||||||
.into_pipeline_data(tag, engine_state.signals().clone()),
|
.into_pipeline_data(tag, engine_state.signals().clone()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ impl Command for Du {
|
|||||||
args,
|
args,
|
||||||
¤t_dir,
|
¤t_dir,
|
||||||
tag,
|
tag,
|
||||||
engine_state.signals(),
|
engine_state.signals().clone(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +163,8 @@ fn du_for_one_pattern(
|
|||||||
args: DuArgs,
|
args: DuArgs,
|
||||||
current_dir: &Path,
|
current_dir: &Path,
|
||||||
span: Span,
|
span: Span,
|
||||||
signals: &Signals,
|
signals: Signals,
|
||||||
) -> Result<impl Iterator<Item = Value> + Send, ShellError> {
|
) -> Result<impl Iterator<Item = Value> + Send, ShellError> {
|
||||||
let signals_clone = signals.clone();
|
|
||||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||||
Pattern::new(x.item.as_ref())
|
Pattern::new(x.item.as_ref())
|
||||||
.map(Some)
|
.map(Some)
|
||||||
@ -176,7 +175,8 @@ fn du_for_one_pattern(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let paths = match args.path {
|
let paths = match args.path {
|
||||||
Some(p) => nu_engine::glob_from(&p, current_dir, span, None),
|
Some(p) => nu_engine::glob_from(&p, current_dir, span, None, signals.clone()),
|
||||||
|
|
||||||
// The * pattern should never fail.
|
// The * pattern should never fail.
|
||||||
None => nu_engine::glob_from(
|
None => nu_engine::glob_from(
|
||||||
&Spanned {
|
&Spanned {
|
||||||
@ -186,6 +186,7 @@ fn du_for_one_pattern(
|
|||||||
current_dir,
|
current_dir,
|
||||||
span,
|
span,
|
||||||
None,
|
None,
|
||||||
|
signals.clone(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
.map(|f| f.1)?;
|
.map(|f| f.1)?;
|
||||||
@ -206,7 +207,7 @@ fn du_for_one_pattern(
|
|||||||
Ok(paths.filter_map(move |p| match p {
|
Ok(paths.filter_map(move |p| match p {
|
||||||
Ok(a) => {
|
Ok(a) => {
|
||||||
if a.is_dir() {
|
if a.is_dir() {
|
||||||
match DirInfo::new(a, ¶ms, max_depth, span, &signals_clone) {
|
match DirInfo::new(a, ¶ms, max_depth, span, &signals) {
|
||||||
Ok(v) => Some(Value::from(v)),
|
Ok(v) => Some(Value::from(v)),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,10 @@ fn ls_for_one_pattern(
|
|||||||
nu_path::expand_path_with(pat.item.as_ref(), &cwd, pat.item.is_expand());
|
nu_path::expand_path_with(pat.item.as_ref(), &cwd, pat.item.is_expand());
|
||||||
// Avoid checking and pushing "*" to the path when directory (do not show contents) flag is true
|
// Avoid checking and pushing "*" to the path when directory (do not show contents) flag is true
|
||||||
if !directory && tmp_expanded.is_dir() {
|
if !directory && tmp_expanded.is_dir() {
|
||||||
if read_dir(tmp_expanded, p_tag, use_threads)?.next().is_none() {
|
if read_dir(tmp_expanded, p_tag, use_threads, signals.clone())?
|
||||||
|
.next()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return Ok(Value::test_nothing().into_pipeline_data());
|
return Ok(Value::test_nothing().into_pipeline_data());
|
||||||
}
|
}
|
||||||
just_read_dir = !(pat.item.is_expand() && nu_glob::is_glob(pat.item.as_ref()));
|
just_read_dir = !(pat.item.is_expand() && nu_glob::is_glob(pat.item.as_ref()));
|
||||||
@ -304,7 +307,10 @@ fn ls_for_one_pattern(
|
|||||||
// Avoid pushing "*" to the default path when directory (do not show contents) flag is true
|
// Avoid pushing "*" to the default path when directory (do not show contents) flag is true
|
||||||
if directory {
|
if directory {
|
||||||
(NuGlob::Expand(".".to_string()), false)
|
(NuGlob::Expand(".".to_string()), false)
|
||||||
} else if read_dir(cwd.clone(), p_tag, use_threads)?.next().is_none() {
|
} else if read_dir(cwd.clone(), p_tag, use_threads, signals.clone())?
|
||||||
|
.next()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
return Ok(Value::test_nothing().into_pipeline_data());
|
return Ok(Value::test_nothing().into_pipeline_data());
|
||||||
} else {
|
} else {
|
||||||
(NuGlob::Expand("*".to_string()), false)
|
(NuGlob::Expand("*".to_string()), false)
|
||||||
@ -317,7 +323,7 @@ fn ls_for_one_pattern(
|
|||||||
let path = pattern_arg.into_spanned(p_tag);
|
let path = pattern_arg.into_spanned(p_tag);
|
||||||
let (prefix, paths) = if just_read_dir {
|
let (prefix, paths) = if just_read_dir {
|
||||||
let expanded = nu_path::expand_path_with(path.item.as_ref(), &cwd, path.item.is_expand());
|
let expanded = nu_path::expand_path_with(path.item.as_ref(), &cwd, path.item.is_expand());
|
||||||
let paths = read_dir(expanded.clone(), p_tag, use_threads)?;
|
let paths = read_dir(expanded.clone(), p_tag, use_threads, signals.clone())?;
|
||||||
// just need to read the directory, so prefix is path itself.
|
// just need to read the directory, so prefix is path itself.
|
||||||
(Some(expanded), paths)
|
(Some(expanded), paths)
|
||||||
} else {
|
} else {
|
||||||
@ -330,11 +336,13 @@ fn ls_for_one_pattern(
|
|||||||
};
|
};
|
||||||
Some(glob_options)
|
Some(glob_options)
|
||||||
};
|
};
|
||||||
glob_from(&path, &cwd, call_span, glob_options)?
|
glob_from(&path, &cwd, call_span, glob_options, signals.clone())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut paths_peek = paths.peekable();
|
let mut paths_peek = paths.peekable();
|
||||||
if paths_peek.peek().is_none() {
|
let no_matches = paths_peek.peek().is_none();
|
||||||
|
signals.check(call_span)?;
|
||||||
|
if no_matches {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: format!("No matches found for {:?}", path.item),
|
error: format!("No matches found for {:?}", path.item),
|
||||||
msg: "Pattern, file or folder not found".into(),
|
msg: "Pattern, file or folder not found".into(),
|
||||||
@ -959,17 +967,21 @@ fn read_dir(
|
|||||||
f: PathBuf,
|
f: PathBuf,
|
||||||
span: Span,
|
span: Span,
|
||||||
use_threads: bool,
|
use_threads: bool,
|
||||||
|
signals: Signals,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<PathBuf, ShellError>> + Send>, ShellError> {
|
) -> Result<Box<dyn Iterator<Item = Result<PathBuf, ShellError>> + Send>, ShellError> {
|
||||||
|
let signals_clone = signals.clone();
|
||||||
let items = f
|
let items = f
|
||||||
.read_dir()
|
.read_dir()
|
||||||
.map_err(|err| IoError::new(err.kind(), span, f.clone()))?
|
.map_err(|err| IoError::new(err.kind(), span, f.clone()))?
|
||||||
.map(move |d| {
|
.map(move |d| {
|
||||||
|
signals_clone.check(span)?;
|
||||||
d.map(|r| r.path())
|
d.map(|r| r.path())
|
||||||
.map_err(|err| IoError::new(err.kind(), span, f.clone()))
|
.map_err(|err| IoError::new(err.kind(), span, f.clone()))
|
||||||
.map_err(ShellError::from)
|
.map_err(ShellError::from)
|
||||||
});
|
});
|
||||||
if !use_threads {
|
if !use_threads {
|
||||||
let mut collected = items.collect::<Vec<_>>();
|
let mut collected = items.collect::<Vec<_>>();
|
||||||
|
signals.check(span)?;
|
||||||
collected.sort_by(|a, b| match (a, b) {
|
collected.sort_by(|a, b| match (a, b) {
|
||||||
(Ok(a), Ok(b)) => a.cmp(b),
|
(Ok(a), Ok(b)) => a.cmp(b),
|
||||||
(Ok(_), Err(_)) => Ordering::Greater,
|
(Ok(_), Err(_)) => Ordering::Greater,
|
||||||
|
@ -95,7 +95,8 @@ impl Command for Open {
|
|||||||
let arg_span = path.span;
|
let arg_span = path.span;
|
||||||
// let path_no_whitespace = &path.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
// let path_no_whitespace = &path.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||||
|
|
||||||
for path in nu_engine::glob_from(&path, &cwd, call_span, None)
|
for path in
|
||||||
|
nu_engine::glob_from(&path, &cwd, call_span, None, engine_state.signals().clone())
|
||||||
.map_err(|err| match err {
|
.map_err(|err| match err {
|
||||||
ShellError::Io(mut err) => {
|
ShellError::Io(mut err) => {
|
||||||
err.kind = err.kind.not_found_as(NotFound::File);
|
err.kind = err.kind.not_found_as(NotFound::File);
|
||||||
|
@ -260,6 +260,7 @@ fn rm(
|
|||||||
require_literal_leading_dot: true,
|
require_literal_leading_dot: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
engine_state.signals().clone(),
|
||||||
) {
|
) {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
for file in files.1 {
|
for file in files.1 {
|
||||||
|
@ -193,7 +193,7 @@ impl Command for UCp {
|
|||||||
for mut p in paths {
|
for mut p in paths {
|
||||||
p.item = p.item.strip_ansi_string_unlikely();
|
p.item = p.item.strip_ansi_string_unlikely();
|
||||||
let exp_files: Vec<Result<PathBuf, ShellError>> =
|
let exp_files: Vec<Result<PathBuf, ShellError>> =
|
||||||
nu_engine::glob_from(&p, &cwd, call.head, None)
|
nu_engine::glob_from(&p, &cwd, call.head, None, engine_state.signals().clone())
|
||||||
.map(|f| f.1)?
|
.map(|f| f.1)?
|
||||||
.collect();
|
.collect();
|
||||||
if exp_files.is_empty() {
|
if exp_files.is_empty() {
|
||||||
|
@ -134,7 +134,7 @@ impl Command for UMv {
|
|||||||
for mut p in paths {
|
for mut p in paths {
|
||||||
p.item = p.item.strip_ansi_string_unlikely();
|
p.item = p.item.strip_ansi_string_unlikely();
|
||||||
let exp_files: Vec<Result<PathBuf, ShellError>> =
|
let exp_files: Vec<Result<PathBuf, ShellError>> =
|
||||||
nu_engine::glob_from(&p, &cwd, call.head, None)
|
nu_engine::glob_from(&p, &cwd, call.head, None, engine_state.signals().clone())
|
||||||
.map(|f| f.1)?
|
.map(|f| f.1)?
|
||||||
.collect();
|
.collect();
|
||||||
if exp_files.is_empty() {
|
if exp_files.is_empty() {
|
||||||
|
@ -158,7 +158,10 @@ impl Command for UTouch {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut expanded_globs = glob(&file_path.to_string_lossy())
|
let mut expanded_globs = glob(
|
||||||
|
&file_path.to_string_lossy(),
|
||||||
|
Some(engine_state.signals().clone()),
|
||||||
|
)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to process file path: {}",
|
"Failed to process file path: {}",
|
||||||
|
@ -382,7 +382,12 @@ pub fn eval_external_arguments(
|
|||||||
match arg {
|
match arg {
|
||||||
// Expand globs passed to run-external
|
// Expand globs passed to run-external
|
||||||
Value::Glob { val, no_expand, .. } if !no_expand => args.extend(
|
Value::Glob { val, no_expand, .. } if !no_expand => args.extend(
|
||||||
expand_glob(&val, cwd.as_std_path(), span, engine_state.signals())?
|
expand_glob(
|
||||||
|
&val,
|
||||||
|
cwd.as_std_path(),
|
||||||
|
span,
|
||||||
|
engine_state.signals().clone(),
|
||||||
|
)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.into_spanned(span)),
|
.map(|s| s.into_spanned(span)),
|
||||||
),
|
),
|
||||||
@ -415,7 +420,7 @@ fn expand_glob(
|
|||||||
arg: &str,
|
arg: &str,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
span: Span,
|
span: Span,
|
||||||
signals: &Signals,
|
signals: Signals,
|
||||||
) -> Result<Vec<OsString>, ShellError> {
|
) -> Result<Vec<OsString>, ShellError> {
|
||||||
// For an argument that isn't a glob, just do the `expand_tilde`
|
// For an argument that isn't a glob, just do the `expand_tilde`
|
||||||
// and `expand_ndots` expansion
|
// and `expand_ndots` expansion
|
||||||
@ -427,7 +432,7 @@ fn expand_glob(
|
|||||||
// We must use `nu_engine::glob_from` here, in order to ensure we get paths from the correct
|
// We must use `nu_engine::glob_from` here, in order to ensure we get paths from the correct
|
||||||
// dir
|
// dir
|
||||||
let glob = NuGlob::Expand(arg.to_owned()).into_spanned(span);
|
let glob = NuGlob::Expand(arg.to_owned()).into_spanned(span);
|
||||||
if let Ok((prefix, matches)) = nu_engine::glob_from(&glob, cwd, span, None) {
|
if let Ok((prefix, matches)) = nu_engine::glob_from(&glob, cwd, span, None, signals.clone()) {
|
||||||
let mut result: Vec<OsString> = vec![];
|
let mut result: Vec<OsString> = vec![];
|
||||||
|
|
||||||
for m in matches {
|
for m in matches {
|
||||||
@ -740,30 +745,30 @@ mod test {
|
|||||||
|
|
||||||
let cwd = dirs.test().as_std_path();
|
let cwd = dirs.test().as_std_path();
|
||||||
|
|
||||||
let actual = expand_glob("*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("*.txt", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let expected = &["a.txt", "b.txt"];
|
let expected = &["a.txt", "b.txt"];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob("./*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("./*.txt", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob("'*.txt'", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("'*.txt'", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let expected = &["'*.txt'"];
|
let expected = &["'*.txt'"];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob(".", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob(".", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let expected = &["."];
|
let expected = &["."];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob("./a.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("./a.txt", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let expected = &["./a.txt"];
|
let expected = &["./a.txt"];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob("[*.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("[*.txt", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let expected = &["[*.txt"];
|
let expected = &["[*.txt"];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
|
||||||
let actual = expand_glob("~/foo.txt", cwd, Span::unknown(), &Signals::empty()).unwrap();
|
let actual = expand_glob("~/foo.txt", cwd, Span::unknown(), Signals::empty()).unwrap();
|
||||||
let home = dirs::home_dir().expect("failed to get home dir");
|
let home = dirs::home_dir().expect("failed to get home dir");
|
||||||
let expected: Vec<OsString> = vec![home.join("foo.txt").into()];
|
let expected: Vec<OsString> = vec![home.join("foo.txt").into()];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_glob::MatchOptions;
|
use nu_glob::MatchOptions;
|
||||||
use nu_path::{canonicalize_with, expand_path_with};
|
use nu_path::{canonicalize_with, expand_path_with};
|
||||||
use nu_protocol::{shell_error::io::IoError, NuGlob, ShellError, Span, Spanned};
|
use nu_protocol::{shell_error::io::IoError, NuGlob, ShellError, Signals, Span, Spanned};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Component, Path, PathBuf},
|
path::{Component, Path, PathBuf},
|
||||||
@ -19,6 +19,7 @@ pub fn glob_from(
|
|||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
span: Span,
|
span: Span,
|
||||||
options: Option<MatchOptions>,
|
options: Option<MatchOptions>,
|
||||||
|
signals: Signals,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
Option<PathBuf>,
|
Option<PathBuf>,
|
||||||
@ -90,7 +91,7 @@ pub fn glob_from(
|
|||||||
let pattern = pattern.to_string_lossy().to_string();
|
let pattern = pattern.to_string_lossy().to_string();
|
||||||
let glob_options = options.unwrap_or_default();
|
let glob_options = options.unwrap_or_default();
|
||||||
|
|
||||||
let glob = nu_glob::glob_with(&pattern, glob_options).map_err(|e| {
|
let glob = nu_glob::glob_with(&pattern, glob_options, signals).map_err(|e| {
|
||||||
nu_protocol::ShellError::GenericError {
|
nu_protocol::ShellError::GenericError {
|
||||||
error: "Error extracting glob pattern".into(),
|
error: "Error extracting glob pattern".into(),
|
||||||
msg: e.to_string(),
|
msg: e.to_string(),
|
||||||
|
@ -13,6 +13,9 @@ categories = ["filesystem"]
|
|||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
doc-comment = "0.3"
|
doc-comment = "0.3"
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use nu_glob::glob;
|
//! use nu_glob::glob;
|
||||||
//!
|
//!
|
||||||
//! for entry in glob("/media/**/*.jpg").expect("Failed to read glob pattern") {
|
//! for entry in glob("/media/**/*.jpg", None).expect("Failed to read glob pattern") {
|
||||||
//! match entry {
|
//! match entry {
|
||||||
//! Ok(path) => println!("{:?}", path.display()),
|
//! Ok(path) => println!("{:?}", path.display()),
|
||||||
//! Err(e) => println!("{:?}", e),
|
//! Err(e) => println!("{:?}", e),
|
||||||
@ -44,6 +44,7 @@
|
|||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use nu_glob::glob_with;
|
//! use nu_glob::glob_with;
|
||||||
//! use nu_glob::MatchOptions;
|
//! use nu_glob::MatchOptions;
|
||||||
|
//! use nu_protocol::Signals;
|
||||||
//!
|
//!
|
||||||
//! let options = MatchOptions {
|
//! let options = MatchOptions {
|
||||||
//! case_sensitive: false,
|
//! case_sensitive: false,
|
||||||
@ -51,7 +52,7 @@
|
|||||||
//! require_literal_leading_dot: false,
|
//! require_literal_leading_dot: false,
|
||||||
//! recursive_match_hidden_dir: true,
|
//! recursive_match_hidden_dir: true,
|
||||||
//! };
|
//! };
|
||||||
//! for entry in glob_with("local/*a*", options).unwrap() {
|
//! for entry in glob_with("local/*a*", options, Signals::empty()).unwrap() {
|
||||||
//! if let Ok(path) = entry {
|
//! if let Ok(path) = entry {
|
||||||
//! println!("{:?}", path.display())
|
//! println!("{:?}", path.display())
|
||||||
//! }
|
//! }
|
||||||
@ -72,6 +73,7 @@ extern crate doc_comment;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
doctest!("../README.md");
|
doctest!("../README.md");
|
||||||
|
|
||||||
|
use nu_protocol::Signals;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@ -102,6 +104,7 @@ pub struct Paths {
|
|||||||
options: MatchOptions,
|
options: MatchOptions,
|
||||||
todo: Vec<Result<(PathBuf, usize), GlobError>>,
|
todo: Vec<Result<(PathBuf, usize), GlobError>>,
|
||||||
scope: Option<PathBuf>,
|
scope: Option<PathBuf>,
|
||||||
|
signals: Signals,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Paths {
|
impl Paths {
|
||||||
@ -113,6 +116,7 @@ impl Paths {
|
|||||||
options: MatchOptions::default(),
|
options: MatchOptions::default(),
|
||||||
todo: vec![Ok((path.to_path_buf(), 0))],
|
todo: vec![Ok((path.to_path_buf(), 0))],
|
||||||
scope: Some(relative_to.into()),
|
scope: Some(relative_to.into()),
|
||||||
|
signals: Signals::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +148,7 @@ impl Paths {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use nu_glob::glob;
|
/// use nu_glob::glob;
|
||||||
///
|
///
|
||||||
/// for entry in glob("/media/pictures/*.jpg").unwrap() {
|
/// for entry in glob("/media/pictures/*.jpg", None).unwrap() {
|
||||||
/// match entry {
|
/// match entry {
|
||||||
/// Ok(path) => println!("{:?}", path.display()),
|
/// Ok(path) => println!("{:?}", path.display()),
|
||||||
///
|
///
|
||||||
@ -169,13 +173,17 @@ impl Paths {
|
|||||||
/// use nu_glob::glob;
|
/// use nu_glob::glob;
|
||||||
/// use std::result::Result;
|
/// use std::result::Result;
|
||||||
///
|
///
|
||||||
/// for path in glob("/media/pictures/*.jpg").unwrap().filter_map(Result::ok) {
|
/// for path in glob("/media/pictures/*.jpg", None).unwrap().filter_map(Result::ok) {
|
||||||
/// println!("{}", path.display());
|
/// println!("{}", path.display());
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// Paths are yielded in alphabetical order.
|
/// Paths are yielded in alphabetical order.
|
||||||
pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
|
pub fn glob(pattern: &str, signals: Option<Signals>) -> Result<Paths, PatternError> {
|
||||||
glob_with(pattern, MatchOptions::default())
|
glob_with(
|
||||||
|
pattern,
|
||||||
|
MatchOptions::default(),
|
||||||
|
signals.unwrap_or(Signals::empty()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an iterator that produces all the `Path`s that match the given
|
/// Return an iterator that produces all the `Path`s that match the given
|
||||||
@ -191,7 +199,11 @@ pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
|
|||||||
/// passed to this function.
|
/// passed to this function.
|
||||||
///
|
///
|
||||||
/// Paths are yielded in alphabetical order.
|
/// Paths are yielded in alphabetical order.
|
||||||
pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternError> {
|
pub fn glob_with(
|
||||||
|
pattern: &str,
|
||||||
|
options: MatchOptions,
|
||||||
|
signals: Signals,
|
||||||
|
) -> Result<Paths, PatternError> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn check_windows_verbatim(p: &Path) -> bool {
|
fn check_windows_verbatim(p: &Path) -> bool {
|
||||||
match p.components().next() {
|
match p.components().next() {
|
||||||
@ -253,6 +265,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternE
|
|||||||
options,
|
options,
|
||||||
todo: Vec::new(),
|
todo: Vec::new(),
|
||||||
scope: None,
|
scope: None,
|
||||||
|
signals,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +297,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternE
|
|||||||
options,
|
options,
|
||||||
todo,
|
todo,
|
||||||
scope: Some(scope),
|
scope: Some(scope),
|
||||||
|
signals,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,8 +312,9 @@ pub fn glob_with_parent(
|
|||||||
pattern: &str,
|
pattern: &str,
|
||||||
options: MatchOptions,
|
options: MatchOptions,
|
||||||
parent: &Path,
|
parent: &Path,
|
||||||
|
signals: Signals,
|
||||||
) -> Result<Paths, PatternError> {
|
) -> Result<Paths, PatternError> {
|
||||||
match glob_with(pattern, options) {
|
match glob_with(pattern, options, signals) {
|
||||||
Ok(mut p) => {
|
Ok(mut p) => {
|
||||||
p.scope = match p.scope {
|
p.scope = match p.scope {
|
||||||
None => Some(parent.to_path_buf()),
|
None => Some(parent.to_path_buf()),
|
||||||
@ -408,7 +423,14 @@ impl Iterator for Paths {
|
|||||||
|
|
||||||
// if there's one prefilled result, take it, otherwise fill the todo buffer
|
// if there's one prefilled result, take it, otherwise fill the todo buffer
|
||||||
if self.todo.len() != 1 {
|
if self.todo.len() != 1 {
|
||||||
fill_todo(&mut self.todo, &self.dir_patterns, 0, &scope, self.options);
|
fill_todo(
|
||||||
|
&mut self.todo,
|
||||||
|
&self.dir_patterns,
|
||||||
|
0,
|
||||||
|
&scope,
|
||||||
|
self.options,
|
||||||
|
&self.signals,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,6 +487,7 @@ impl Iterator for Paths {
|
|||||||
next,
|
next,
|
||||||
&path,
|
&path,
|
||||||
self.options,
|
self.options,
|
||||||
|
&self.signals,
|
||||||
);
|
);
|
||||||
|
|
||||||
if next == self.dir_patterns.len() - 1 {
|
if next == self.dir_patterns.len() - 1 {
|
||||||
@ -516,6 +539,7 @@ impl Iterator for Paths {
|
|||||||
idx + 1,
|
idx + 1,
|
||||||
&path,
|
&path,
|
||||||
self.options,
|
self.options,
|
||||||
|
&self.signals,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -905,6 +929,7 @@ fn fill_todo(
|
|||||||
idx: usize,
|
idx: usize,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
options: MatchOptions,
|
options: MatchOptions,
|
||||||
|
signals: &Signals,
|
||||||
) {
|
) {
|
||||||
// convert a pattern that's just many Char(_) to a string
|
// convert a pattern that's just many Char(_) to a string
|
||||||
fn pattern_as_str(pattern: &Pattern) -> Option<String> {
|
fn pattern_as_str(pattern: &Pattern) -> Option<String> {
|
||||||
@ -926,7 +951,7 @@ fn fill_todo(
|
|||||||
// . or .. globs since these never show up as path components.
|
// . or .. globs since these never show up as path components.
|
||||||
todo.push(Ok((next_path, !0)));
|
todo.push(Ok((next_path, !0)));
|
||||||
} else {
|
} else {
|
||||||
fill_todo(todo, patterns, idx + 1, &next_path, options);
|
fill_todo(todo, patterns, idx + 1, &next_path, options, signals);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -957,6 +982,9 @@ fn fill_todo(
|
|||||||
None if is_dir => {
|
None if is_dir => {
|
||||||
let dirs = fs::read_dir(path).and_then(|d| {
|
let dirs = fs::read_dir(path).and_then(|d| {
|
||||||
d.map(|e| {
|
d.map(|e| {
|
||||||
|
if signals.interrupted() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::Interrupted));
|
||||||
|
}
|
||||||
e.map(|e| {
|
e.map(|e| {
|
||||||
if curdir {
|
if curdir {
|
||||||
PathBuf::from(
|
PathBuf::from(
|
||||||
@ -1113,9 +1141,15 @@ impl Default for MatchOptions {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{glob, MatchOptions, Pattern};
|
use crate::{Paths, PatternError};
|
||||||
|
|
||||||
|
use super::{glob as glob_with_signals, MatchOptions, Pattern};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn glob(pattern: &str) -> Result<Paths, PatternError> {
|
||||||
|
glob_with_signals(pattern, None)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pattern_from_str() {
|
fn test_pattern_from_str() {
|
||||||
assert!("a*b".parse::<Pattern>().unwrap().matches("a_b"));
|
assert!("a*b".parse::<Pattern>().unwrap().matches("a_b"));
|
||||||
|
@ -42,7 +42,7 @@ fn find_nu_scripts_in_folder(folder_uri: &Uri) -> Result<nu_glob::Paths> {
|
|||||||
return Err(miette!("\nworkspace folder does not exist."));
|
return Err(miette!("\nworkspace folder does not exist."));
|
||||||
}
|
}
|
||||||
let pattern = format!("{}/**/*.nu", path.to_string_lossy());
|
let pattern = format!("{}/**/*.nu", path.to_string_lossy());
|
||||||
nu_glob::glob(&pattern).into_diagnostic()
|
nu_glob::glob(&pattern, None).into_diagnostic()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageServer {
|
impl LanguageServer {
|
||||||
|
@ -231,7 +231,7 @@ impl Playground<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn glob_vec(pattern: &str) -> Vec<std::path::PathBuf> {
|
pub fn glob_vec(pattern: &str) -> Vec<std::path::PathBuf> {
|
||||||
let glob = glob(pattern);
|
let glob = glob(pattern, None);
|
||||||
|
|
||||||
glob.expect("invalid pattern")
|
glob.expect("invalid pattern")
|
||||||
.map(|path| {
|
.map(|path| {
|
||||||
|
Loading…
Reference in New Issue
Block a user