Skip to content

Commit 6ed2348

Browse files
pascalkutheg-re-gthe-mikedavis
authored
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>
1 parent 62d046f commit 6ed2348

File tree

5 files changed

+47
-14
lines changed

5 files changed

+47
-14
lines changed

book/src/configuration.md

+2
Original file line numberDiff line numberDiff line change
@@ -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
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ use movement::Movement;
4949
use 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
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ pub mod health;
1010
pub mod job;
1111
pub mod keymap;
1212
pub mod ui;
13+
use std::path::Path;
14+
15+
use ignore::DirEntry;
1316
pub use keymap::macros::*;
1417

1518
#[cfg(not(windows))]
@@ -22,3 +25,25 @@ fn true_color() -> bool {
2225
fn 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
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod statusline;
1515
mod text;
1616

1717
use crate::compositor::{Component, Compositor};
18+
use crate::filter_picker_entry;
1819
use crate::job::{self, Callback};
1920
pub use completion::Completion;
2021
pub 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
Original file line numberDiff line numberDiff line change
@@ -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,

0 commit comments

Comments
 (0)