Don't ignore any regular files when globbing

pull/9723/head
mo8it 7 months ago
parent 5be6c211c5
commit bb4fef67bd

@ -1,11 +1,12 @@
use std::fmt::Write;
use std::fs;
use std::ops::Deref;
use crate::job::Job;
use super::*;
use globset::{Glob, GlobBuilder, GlobSet};
use globset::{Glob, GlobBuilder, GlobSetBuilder};
use helix_core::fuzzy::fuzzy_match;
use helix_core::indent::MAX_INDENT;
use helix_core::{encoding, line_ending, shellwords::Shellwords};
@ -136,10 +137,12 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
for arg in args {
let (path, pos) = args::parse_file(arg);
let path = helix_stdx::path::canonicalize(path);
if let Ok(metadata) = path.metadata() {
// Shortcut for opening an existing path without globbing
if let Ok(metadata) = fs::metadata(&path) {
// Path exists
// Shortcut for opening a path without globbing
if metadata.is_dir() {
let file_type = metadata.file_type();
if file_type.is_dir() {
// If the path is a directory, open a file picker on that directory
let callback = async move {
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
@ -151,71 +154,86 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
Ok(call)
};
cx.jobs.callback(callback);
} else {
} else if file_type.is_file() {
open_file(cx, &path, pos)?;
} else {
bail!("{} is not a regular file", path.display());
}
continue;
}
let mut glob_set_builder = GlobSet::builder();
glob_set_builder.add(glob(&path)?);
let file_glob_set = GlobSetBuilder::new()
.add(glob(&path)?)
.build()
.context("invalid glob")?;
let mut parent_dirs_glob_set_builder = GlobSetBuilder::new();
let mut root = None;
let mut comps = path.components();
// Iterate over all parents
while comps.next_back().is_some() {
let parent = comps.as_path();
if parent.as_os_str().is_empty() {
// No parents left
break;
}
// Add each parent as a glob for filtering in the recursive walker
glob_set_builder.add(glob(parent)?);
parent_dirs_glob_set_builder.add(glob(parent)?);
if root.is_none() && parent.exists() {
// Found the first parent that exists
root = Some(parent.to_path_buf());
break;
}
}
let glob_set = glob_set_builder.build().context("invalid glob")?;
let root = root.unwrap_or_else(current_working_dir);
let parent_dirs_glob_set = parent_dirs_glob_set_builder
.build()
.context("invalid glob")?;
let mut walk = cx
.editor
.config()
.file_picker
.walk_builder(root)
.filter_entry(move |entry| glob_set.is_match(entry.path()))
.build();
// Call `next` to ignore the root directory which is yielded first
if walk.next().is_none() {
let root = root.unwrap_or_else(current_working_dir);
let Ok(dir_reader) = fs::read_dir(root) else {
continue;
}
};
let mut dir_reader_stack = Vec::with_capacity(32);
dir_reader_stack.push(dir_reader);
let mut to_open = Vec::with_capacity(GLOBBING_MAX_N_FILES);
for entry in walk {
let entry = entry.context("recursive walking failed")?;
let Some(file_type) = entry.file_type() else {
// Entry is stdin
// Recursion with a "stack" (on the heap) instead of a recursive function
'outer: while let Some(dir_reader) = dir_reader_stack.last_mut() {
for entry in dir_reader {
let Ok(entry) = entry else {
continue;
};
// Don't open directories when globbing
let path = entry.path();
let Ok(metadata) = fs::metadata(&path) else {
continue;
};
let file_type = metadata.file_type();
if file_type.is_dir() {
if !parent_dirs_glob_set.is_match(&path) {
continue;
}
let Ok(dir_reader) = fs::read_dir(path) else {
continue;
};
dir_reader_stack.push(dir_reader);
continue 'outer;
}
if file_type.is_file() && file_glob_set.is_match(&path) {
if to_open.len() == GLOBBING_MAX_N_FILES {
bail!("tried to open more than {GLOBBING_MAX_N_FILES} files at once");
}
to_open.push(entry.into_path());
to_open.push(path);
}
// Can't be a symlink because `fs::metadata` traverses symlinks
}
dir_reader_stack.pop();
}
if to_open.is_empty() {
// Nothing found to open after globbing.
// Open a new file.
// Nothing found to open after globbing. Open a new file
open_file(cx, &path, pos)?;
continue;
}

Loading…
Cancel
Save