From 6ed2348078a331bc2039a313bd7ad9f0bb1a00c2 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 2 Feb 2023 18:14:02 +0100 Subject: Hide duplicate symlinks from the picker (#5658) * hide duplicate symlinks from the picker * Apply suggestions from code review Co-authored-by: g-re-g <123515925+g-re-g@users.noreply.github.com> * minor stylistic fix Co-authored-by: Michael Davis --------- Co-authored-by: g-re-g <123515925+g-re-g@users.noreply.github.com> Co-authored-by: Michael Davis --- helix-term/src/commands.rs | 13 +++++++++---- helix-term/src/lib.rs | 25 +++++++++++++++++++++++++ helix-term/src/ui/mod.rs | 18 ++++++++---------- 3 files changed, 42 insertions(+), 14 deletions(-) (limited to 'helix-term/src') diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2ee3f6b9..4f27fc80 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -49,6 +49,7 @@ use movement::Movement; use crate::{ args, compositor::{self, Component, Compositor}, + filter_picker_entry, job::Callback, keymap::ReverseKeymap, ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent}, @@ -2013,6 +2014,11 @@ fn global_search(cx: &mut Context) { let search_root = std::env::current_dir() .expect("Global search error: Failed to get current dir"); + let dedup_symlinks = file_picker_config.deduplicate_links; + let absolute_root = search_root + .canonicalize() + .unwrap_or_else(|_| search_root.clone()); + WalkBuilder::new(search_root) .hidden(file_picker_config.hidden) .parents(file_picker_config.parents) @@ -2022,10 +2028,9 @@ fn global_search(cx: &mut Context) { .git_global(file_picker_config.git_global) .git_exclude(file_picker_config.git_exclude) .max_depth(file_picker_config.max_depth) - // We always want to ignore the .git directory, otherwise if - // `ignore` is turned off above, we end up with a lot of noise - // in our picker. - .filter_entry(|entry| entry.file_name() != ".git") + .filter_entry(move |entry| { + filter_picker_entry(entry, &absolute_root, dedup_symlinks) + }) .build_parallel() .run(|| { let mut searcher = searcher.clone(); diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index a945b20d..f0bc9129 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -10,6 +10,9 @@ pub mod health; pub mod job; pub mod keymap; pub mod ui; +use std::path::Path; + +use ignore::DirEntry; pub use keymap::macros::*; #[cfg(not(windows))] @@ -22,3 +25,25 @@ fn true_color() -> bool { fn true_color() -> bool { true } + +/// Function used for filtering dir entries in the various file pickers. +fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> bool { + // We always want to ignore the .git directory, otherwise if + // `ignore` is turned off, we end up with a lot of noise + // in our picker. + if entry.file_name() == ".git" { + return false; + } + + // We also ignore symlinks that point inside the current directory + // if `dedup_links` is enabled. + if dedup_symlinks && entry.path_is_symlink() { + return entry + .path() + .canonicalize() + .ok() + .map_or(false, |path| !path.starts_with(&root)); + } + + true +} diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 5e7f8c36..d7717f8c 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -15,6 +15,7 @@ mod statusline; mod text; use crate::compositor::{Component, Compositor}; +use crate::filter_picker_entry; use crate::job::{self, Callback}; pub use completion::Completion; pub use editor::EditorView; @@ -163,6 +164,9 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi let now = Instant::now(); + let dedup_symlinks = config.file_picker.deduplicate_links; + let absolute_root = root.canonicalize().unwrap_or_else(|_| root.clone()); + let mut walk_builder = WalkBuilder::new(&root); walk_builder .hidden(config.file_picker.hidden) @@ -173,10 +177,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi .git_global(config.file_picker.git_global) .git_exclude(config.file_picker.git_exclude) .max_depth(config.file_picker.max_depth) - // We always want to ignore the .git directory, otherwise if - // `ignore` is turned off above, we end up with a lot of noise - // in our picker. - .filter_entry(|entry| entry.file_name() != ".git"); + .filter_entry(move |entry| filter_picker_entry(entry, &absolute_root, dedup_symlinks)); // We want to exclude files that the editor can't handle yet let mut type_builder = TypesBuilder::new(); @@ -195,14 +196,11 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi // We want files along with their modification date for sorting let files = walk_builder.build().filter_map(|entry| { let entry = entry.ok()?; - // This is faster than entry.path().is_dir() since it uses cached fs::Metadata fetched by ignore/walkdir - let is_dir = entry.file_type().map_or(false, |ft| ft.is_dir()); - if is_dir { - // Will give a false positive if metadata cannot be read (eg. permission error) - None - } else { + if entry.file_type()?.is_file() { Some(entry.into_path()) + } else { + None } }); -- cgit v1.2.3-70-g09d2