on master help sign in
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 <mcarsondavis@gmail.com> --------- Co-authored-by: g-re-g <123515925+g-re-g@users.noreply.github.com> Co-authored-by: Michael Davis <mcarsondavis@gmail.com>

commit6ed2348078a331bc2039a313bd7ad9f0bb1a00c2 authorPascal Kuthe <pascal.kuthe@semimod.de> dateThu 2 Feb 2023, 18:14 (4 years ago) parent62d046f
5 files changed+47−14
book/src/configuration.md+2 helix-term/src/commands.rs+9−4 helix-term/src/lib.rs+25 helix-term/src/ui/mod.rs+8−10 helix-view/src/editor.rs+3
book/src/configuration.md+2
@@ -150,6 +150,8 @@ All git related options are only enabled in a git repository.
150150| Key | Description | Default |
151151|--|--|---------|
152152|`hidden` | Enables ignoring hidden files. | true
153+|`follow-links` | Follow symlinks instead of ignoring them | true
154+|`deduplicate-links` | Ignore symlinks that point at files already shown in the picker | true
153155|`parents` | Enables reading ignore files from parent directories. | true
154156|`ignore` | Enables reading `.ignore` files. | true
155157|`git-ignore` | Enables reading `.gitignore` files. | true
helix-term/src/commands.rs+9−4
@@ -49,6 +49,7 @@ use movement::Movement;
4949use crate::{
5050 args,
5151 compositor::{self, Component, Compositor},
52+ filter_picker_entry,
5253 job::Callback,
5354 keymap::ReverseKeymap,
5455 ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent},
@@ -2013,6 +2014,11 @@ fn global_search(cx: &mut Context) {
20132014
20142015 let search_root = std::env::current_dir()
20152016 .expect("Global search error: Failed to get current dir");
2017+ let dedup_symlinks = file_picker_config.deduplicate_links;
2018+ let absolute_root = search_root
2019+ .canonicalize()
2020+ .unwrap_or_else(|_| search_root.clone());
2021+
20162022 WalkBuilder::new(search_root)
20172023 .hidden(file_picker_config.hidden)
20182024 .parents(file_picker_config.parents)
@@ -2022,10 +2028,9 @@ fn global_search(cx: &mut Context) {
20222028 .git_global(file_picker_config.git_global)
20232029 .git_exclude(file_picker_config.git_exclude)
20242030 .max_depth(file_picker_config.max_depth)
2025− // We always want to ignore the .git directory, otherwise if
2026− // `ignore` is turned off above, we end up with a lot of noise
2027− // in our picker.
2028− .filter_entry(|entry| entry.file_name() != ".git")
2031+ .filter_entry(move |entry| {
2032+ filter_picker_entry(entry, &absolute_root, dedup_symlinks)
2033+ })
20292034 .build_parallel()
20302035 .run(|| {
20312036 let mut searcher = searcher.clone();
helix-term/src/lib.rs+25
@@ -10,6 +10,9 @@ pub mod health;
1010pub mod job;
1111pub mod keymap;
1212pub mod ui;
13+use std::path::Path;
14+
15+use ignore::DirEntry;
1316pub use keymap::macros::*;
1417
1518#[cfg(not(windows))]
@@ -22,3 +25,25 @@ fn true_color() -> bool {
2225fn true_color() -> bool {
2326 true
2427}
28+
29+/// Function used for filtering dir entries in the various file pickers.
30+fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> bool {
31+ // We always want to ignore the .git directory, otherwise if
32+ // `ignore` is turned off, we end up with a lot of noise
33+ // in our picker.
34+ if entry.file_name() == ".git" {
35+ return false;
36+ }
37+
38+ // We also ignore symlinks that point inside the current directory
39+ // if `dedup_links` is enabled.
40+ if dedup_symlinks && entry.path_is_symlink() {
41+ return entry
42+ .path()
43+ .canonicalize()
44+ .ok()
45+ .map_or(false, |path| !path.starts_with(&root));
46+ }
47+
48+ true
49+}
helix-term/src/ui/mod.rs+8−10
@@ -15,6 +15,7 @@ mod statusline;
1515mod text;
1616
1717use crate::compositor::{Component, Compositor};
18+use crate::filter_picker_entry;
1819use crate::job::{self, Callback};
1920pub use completion::Completion;
2021pub use editor::EditorView;
@@ -163,6 +164,9 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
163164
164165 let now = Instant::now();
165166
167+ let dedup_symlinks = config.file_picker.deduplicate_links;
168+ let absolute_root = root.canonicalize().unwrap_or_else(|_| root.clone());
169+
166170 let mut walk_builder = WalkBuilder::new(&root);
167171 walk_builder
168172 .hidden(config.file_picker.hidden)
@@ -173,10 +177,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
173177 .git_global(config.file_picker.git_global)
174178 .git_exclude(config.file_picker.git_exclude)
175179 .max_depth(config.file_picker.max_depth)
176− // We always want to ignore the .git directory, otherwise if
177− // `ignore` is turned off above, we end up with a lot of noise
178− // in our picker.
179− .filter_entry(|entry| entry.file_name() != ".git");
180+ .filter_entry(move |entry| filter_picker_entry(entry, &absolute_root, dedup_symlinks));
180181
181182 // We want to exclude files that the editor can't handle yet
182183 let mut type_builder = TypesBuilder::new();
@@ -195,14 +196,11 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
195196 // We want files along with their modification date for sorting
196197 let files = walk_builder.build().filter_map(|entry| {
197198 let entry = entry.ok()?;
198−
199199 // This is faster than entry.path().is_dir() since it uses cached fs::Metadata fetched by ignore/walkdir
200− let is_dir = entry.file_type().map_or(false, |ft| ft.is_dir());
201− if is_dir {
202− // Will give a false positive if metadata cannot be read (eg. permission error)
203− None
204− } else {
200+ if entry.file_type()?.is_file() {
205201 Some(entry.into_path())
202+ } else {
203+ None
206204 }
207205 });
208206
helix-view/src/editor.rs+3
@@ -173,6 +173,8 @@ pub struct FilePickerConfig {
173173 /// Enables following symlinks.
174174 /// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true.
175175 pub follow_symlinks: bool,
176+ /// Hides symlinks that point into the current directory. Defaults to true.
177+ pub deduplicate_links: bool,
176178 /// Enables reading ignore files from parent directories. Defaults to true.
177179 pub parents: bool,
178180 /// Enables reading `.ignore` files.
@@ -197,6 +199,7 @@ impl Default for FilePickerConfig {
197199 Self {
198200 hidden: true,
199201 follow_symlinks: true,
202+ deduplicate_links: true,
200203 parents: true,
201204 ignore: true,
202205 git_ignore: true,