Skip to content

Commit c2cc203

Browse files
authored
Better handling of symlinks (#2718)
- Add file-picker.follow-symlinks configuration option (default is true), this also controls if filename and directory completers follow symlinks. - Update FilePicker to set editor error if opening a file fails, instead of panicing. Fix #1548 Fix #2246
1 parent 7983c71 commit c2cc203

File tree

2 files changed

+23
-13
lines changed

2 files changed

+23
-13
lines changed

helix-term/src/ui/mod.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
118118
.hidden(config.file_picker.hidden)
119119
.parents(config.file_picker.parents)
120120
.ignore(config.file_picker.ignore)
121+
.follow_links(config.file_picker.follow_symlinks)
121122
.git_ignore(config.file_picker.git_ignore)
122123
.git_global(config.file_picker.git_global)
123124
.git_exclude(config.file_picker.git_exclude)
@@ -146,14 +147,13 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
146147
let entry = entry.ok()?;
147148

148149
// This is faster than entry.path().is_dir() since it uses cached fs::Metadata fetched by ignore/walkdir
149-
let is_dir = entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
150-
150+
let is_dir = entry.file_type().map_or(false, |ft| ft.is_dir());
151151
if is_dir {
152152
// Will give a false positive if metadata cannot be read (eg. permission error)
153-
return None;
153+
None
154+
} else {
155+
Some(entry.into_path())
154156
}
155-
156-
Some(entry.into_path())
157157
});
158158

159159
// Cap the number of files if we aren't in a git project, preventing
@@ -175,9 +175,14 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
175175
path.strip_prefix(&root).unwrap_or(path).to_string_lossy()
176176
},
177177
move |cx, path: &PathBuf, action| {
178-
cx.editor
179-
.open(path.into(), action)
180-
.expect("editor.open failed");
178+
if let Err(e) = cx.editor.open(path.into(), action) {
179+
let err = if let Some(err) = e.source() {
180+
format!("{}", err)
181+
} else {
182+
format!("unable to open \"{}\"", path.display())
183+
};
184+
cx.editor.set_error(err);
185+
}
181186
},
182187
|_editor, path| Some((path.clone(), None)),
183188
)
@@ -281,8 +286,8 @@ pub mod completers {
281286
.collect()
282287
}
283288

284-
pub fn filename(_editor: &Editor, input: &str) -> Vec<Completion> {
285-
filename_impl(input, |entry| {
289+
pub fn filename(editor: &Editor, input: &str) -> Vec<Completion> {
290+
filename_impl(editor, input, |entry| {
286291
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
287292

288293
if is_dir {
@@ -314,8 +319,8 @@ pub mod completers {
314319
.collect()
315320
}
316321

317-
pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
318-
filename_impl(input, |entry| {
322+
pub fn directory(editor: &Editor, input: &str) -> Vec<Completion> {
323+
filename_impl(editor, input, |entry| {
319324
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
320325

321326
if is_dir {
@@ -338,7 +343,7 @@ pub mod completers {
338343
}
339344

340345
// TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
341-
fn filename_impl<F>(input: &str, filter_fn: F) -> Vec<Completion>
346+
fn filename_impl<F>(editor: &Editor, input: &str, filter_fn: F) -> Vec<Completion>
342347
where
343348
F: Fn(&ignore::DirEntry) -> FileMatch,
344349
{
@@ -370,6 +375,7 @@ pub mod completers {
370375

371376
let mut files: Vec<_> = WalkBuilder::new(&dir)
372377
.hidden(false)
378+
.follow_links(editor.config().file_picker.follow_symlinks)
373379
.max_depth(Some(1))
374380
.build()
375381
.filter_map(|file| {

helix-view/src/editor.rs

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub struct FilePickerConfig {
7171
/// Enables ignoring hidden files.
7272
/// Whether to hide hidden files in file picker and global search results. Defaults to true.
7373
pub hidden: bool,
74+
/// Enables following symlinks.
75+
/// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true.
76+
pub follow_symlinks: bool,
7477
/// Enables reading ignore files from parent directories. Defaults to true.
7578
pub parents: bool,
7679
/// Enables reading `.ignore` files.
@@ -94,6 +97,7 @@ impl Default for FilePickerConfig {
9497
fn default() -> Self {
9598
Self {
9699
hidden: true,
100+
follow_symlinks: true,
97101
parents: true,
98102
ignore: true,
99103
git_ignore: true,

0 commit comments

Comments
 (0)