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::fmt::Write;
use std::fs;
use std::ops::Deref; use std::ops::Deref;
use crate::job::Job; use crate::job::Job;
use super::*; use super::*;
use globset::{Glob, GlobBuilder, GlobSet}; use globset::{Glob, GlobBuilder, GlobSetBuilder};
use helix_core::fuzzy::fuzzy_match; use helix_core::fuzzy::fuzzy_match;
use helix_core::indent::MAX_INDENT; use helix_core::indent::MAX_INDENT;
use helix_core::{encoding, line_ending, shellwords::Shellwords}; 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 { for arg in args {
let (path, pos) = args::parse_file(arg); let (path, pos) = args::parse_file(arg);
let path = helix_stdx::path::canonicalize(path); 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 // Path exists
// Shortcut for opening a path without globbing let file_type = metadata.file_type();
if metadata.is_dir() { if file_type.is_dir() {
// If the path is a directory, open a file picker on that directory // If the path is a directory, open a file picker on that directory
let callback = async move { let callback = async move {
let call: job::Callback = job::Callback::EditorCompositor(Box::new( 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) Ok(call)
}; };
cx.jobs.callback(callback); cx.jobs.callback(callback);
} else { } else if file_type.is_file() {
open_file(cx, &path, pos)?; open_file(cx, &path, pos)?;
} else {
bail!("{} is not a regular file", path.display());
} }
continue; continue;
} }
let mut glob_set_builder = GlobSet::builder(); let file_glob_set = GlobSetBuilder::new()
glob_set_builder.add(glob(&path)?); .add(glob(&path)?)
.build()
.context("invalid glob")?;
let mut parent_dirs_glob_set_builder = GlobSetBuilder::new();
let mut root = None; let mut root = None;
let mut comps = path.components(); let mut comps = path.components();
// Iterate over all parents // Iterate over all parents
while comps.next_back().is_some() { while comps.next_back().is_some() {
let parent = comps.as_path(); 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 // 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() { if root.is_none() && parent.exists() {
// Found the first parent that exists // Found the first parent that exists
root = Some(parent.to_path_buf()); root = Some(parent.to_path_buf());
break;
} }
} }
let glob_set = glob_set_builder.build().context("invalid glob")?; let parent_dirs_glob_set = parent_dirs_glob_set_builder
let root = root.unwrap_or_else(current_working_dir); .build()
.context("invalid glob")?;
let mut walk = cx let root = root.unwrap_or_else(current_working_dir);
.editor let Ok(dir_reader) = fs::read_dir(root) else {
.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() {
continue; 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); let mut to_open = Vec::with_capacity(GLOBBING_MAX_N_FILES);
for entry in walk {
let entry = entry.context("recursive walking failed")?; // Recursion with a "stack" (on the heap) instead of a recursive function
let Some(file_type) = entry.file_type() else { 'outer: while let Some(dir_reader) = dir_reader_stack.last_mut() {
// Entry is stdin for entry in dir_reader {
let Ok(entry) = entry else {
continue; 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 file_type.is_dir() {
if !parent_dirs_glob_set.is_match(&path) {
continue; 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 { if to_open.len() == GLOBBING_MAX_N_FILES {
bail!("tried to open more than {GLOBBING_MAX_N_FILES} files at once"); 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() { if to_open.is_empty() {
// Nothing found to open after globbing. // Nothing found to open after globbing. Open a new file
// Open a new file.
open_file(cx, &path, pos)?; open_file(cx, &path, pos)?;
continue; continue;
} }

Loading…
Cancel
Save