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:
Solomon 2025-02-28 11:36:39 -07:00 committed by GitHub
parent 48bdcc71f4
commit c5a14bb8ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 120 additions and 58 deletions

1
Cargo.lock generated
View File

@ -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]]

View File

@ -118,7 +118,7 @@ impl Command for Du {
min_size, min_size,
}; };
Ok( Ok(
du_for_one_pattern(args, &current_dir, tag, engine_state.signals())? du_for_one_pattern(args, &current_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,
&current_dir, &current_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, &params, max_depth, span, &signals_clone) { match DirInfo::new(a, &params, max_depth, span, &signals) {
Ok(v) => Some(Value::from(v)), Ok(v) => Some(Value::from(v)),
Err(_) => None, Err(_) => None,
} }

View File

@ -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,

View File

@ -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);

View 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 {

View File

@ -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() {

View File

@ -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() {

View File

@ -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: {}",

View File

@ -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);

View File

@ -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(),

View File

@ -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"

View File

@ -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"));

View File

@ -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 {

View File

@ -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| {