Skip to content

Commit 4369da6

Browse files
committed
hide duplicate symlinks from the picker
1 parent 17acadb commit 4369da6

File tree

5 files changed

+46
-15
lines changed

5 files changed

+46
-15
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+
|`deadup-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

+7-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use movement::Movement;
4545
use crate::{
4646
args,
4747
compositor::{self, Component, Compositor},
48+
filter_entry,
4849
job::Callback,
4950
keymap::ReverseKeymap,
5051
ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent},
@@ -1913,6 +1914,11 @@ fn global_search(cx: &mut Context) {
19131914

19141915
let search_root = std::env::current_dir()
19151916
.expect("Global search error: Failed to get current dir");
1917+
let deadup_symlinks = file_picker_config.deadup_symlinks;
1918+
let absolute_root = search_root
1919+
.canonicalize()
1920+
.unwrap_or_else(|_| search_root.clone());
1921+
19161922
WalkBuilder::new(search_root)
19171923
.hidden(file_picker_config.hidden)
19181924
.parents(file_picker_config.parents)
@@ -1922,10 +1928,7 @@ fn global_search(cx: &mut Context) {
19221928
.git_global(file_picker_config.git_global)
19231929
.git_exclude(file_picker_config.git_exclude)
19241930
.max_depth(file_picker_config.max_depth)
1925-
// We always want to ignore the .git directory, otherwise if
1926-
// `ignore` is turned off above, we end up with a lot of noise
1927-
// in our picker.
1928-
.filter_entry(|entry| entry.file_name() != ".git")
1931+
.filter_entry(move |entry| filter_entry(entry, &absolute_root, deadup_symlinks))
19291932
.build_parallel()
19301933
.run(|| {
19311934
let mut searcher = searcher.clone();

helix-term/src/lib.rs

+26
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,26 @@ 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+
///
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+
/// We also ignore symlinks that point inside the current directory
35+
/// if `deadup_symlinks` is enabled.
36+
fn filter_entry(entry: &DirEntry, root: &Path, deadup_symlinks: bool) -> bool {
37+
if entry.file_name() != ".git" {
38+
return false;
39+
}
40+
41+
if deadup_symlinks && entry.path_is_symlink() {
42+
return entry
43+
.path()
44+
.canonicalize()
45+
.ok()
46+
.map_or(false, |path| !path.starts_with(&root));
47+
}
48+
49+
true
50+
}

helix-term/src/ui/mod.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod statusline;
1414
mod text;
1515

1616
use crate::compositor::{Component, Compositor};
17+
use crate::filter_entry;
1718
use crate::job::{self, Callback};
1819
pub use completion::Completion;
1920
pub use editor::EditorView;
@@ -162,6 +163,9 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
162163

163164
let now = Instant::now();
164165

166+
let deadup_symlinks = config.file_picker.deadup_symlinks;
167+
let absolute_root = root.canonicalize().unwrap_or_else(|_| root.clone());
168+
165169
let mut walk_builder = WalkBuilder::new(&root);
166170
walk_builder
167171
.hidden(config.file_picker.hidden)
@@ -172,10 +176,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
172176
.git_global(config.file_picker.git_global)
173177
.git_exclude(config.file_picker.git_exclude)
174178
.max_depth(config.file_picker.max_depth)
175-
// We always want to ignore the .git directory, otherwise if
176-
// `ignore` is turned off above, we end up with a lot of noise
177-
// in our picker.
178-
.filter_entry(|entry| entry.file_name() != ".git");
179+
.filter_entry(move |entry| filter_entry(entry, &absolute_root, deadup_symlinks));
179180

180181
// We want to exclude files that the editor can't handle yet
181182
let mut type_builder = TypesBuilder::new();
@@ -194,15 +195,11 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
194195
// We want files along with their modification date for sorting
195196
let files = walk_builder.build().filter_map(|entry| {
196197
let entry = entry.ok()?;
197-
198198
// This is faster than entry.path().is_dir() since it uses cached fs::Metadata fetched by ignore/walkdir
199-
let is_dir = entry.file_type().map_or(false, |ft| ft.is_dir());
200-
if is_dir {
201-
// Will give a false positive if metadata cannot be read (eg. permission error)
202-
None
203-
} else {
204-
Some(entry.into_path())
199+
if !entry.file_type()?.is_file() {
200+
return None;
205201
}
202+
Some(entry.into_path())
206203
});
207204

208205
// Cap the number of files if we aren't in a git project, preventing

helix-view/src/editor.rs

+3
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ pub struct FilePickerConfig {
171171
/// Enables following symlinks.
172172
/// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true.
173173
pub follow_symlinks: bool,
174+
/// Hides symlinks that point into the current directory. Default to true
175+
pub deadup_symlinks: bool,
174176
/// Enables reading ignore files from parent directories. Defaults to true.
175177
pub parents: bool,
176178
/// Enables reading `.ignore` files.
@@ -195,6 +197,7 @@ impl Default for FilePickerConfig {
195197
Self {
196198
hidden: true,
197199
follow_symlinks: true,
200+
deadup_symlinks: true,
198201
parents: true,
199202
ignore: true,
200203
git_ignore: true,

0 commit comments

Comments
 (0)