From c2cc2037b548a39d374b11c6919e10b1a7115cfb Mon Sep 17 00:00:00 2001 From: Roland Kovacs Date: Wed, 15 Jun 2022 06:17:17 +0200 Subject: [PATCH] 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 --- helix-term/src/ui/mod.rs | 32 +++++++++++++++++++------------- helix-view/src/editor.rs | 4 ++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 6dea21924..23d0dca0a 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -118,6 +118,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi .hidden(config.file_picker.hidden) .parents(config.file_picker.parents) .ignore(config.file_picker.ignore) + .follow_links(config.file_picker.follow_symlinks) .git_ignore(config.file_picker.git_ignore) .git_global(config.file_picker.git_global) .git_exclude(config.file_picker.git_exclude) @@ -146,14 +147,13 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi 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(|ft| ft.is_dir()).unwrap_or(false); - + 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) - return None; + None + } else { + Some(entry.into_path()) } - - Some(entry.into_path()) }); // 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 path.strip_prefix(&root).unwrap_or(path).to_string_lossy() }, move |cx, path: &PathBuf, action| { - cx.editor - .open(path.into(), action) - .expect("editor.open failed"); + if let Err(e) = cx.editor.open(path.into(), action) { + let err = if let Some(err) = e.source() { + format!("{}", err) + } else { + format!("unable to open \"{}\"", path.display()) + }; + cx.editor.set_error(err); + } }, |_editor, path| Some((path.clone(), None)), ) @@ -281,8 +286,8 @@ pub mod completers { .collect() } - pub fn filename(_editor: &Editor, input: &str) -> Vec { - filename_impl(input, |entry| { + pub fn filename(editor: &Editor, input: &str) -> Vec { + filename_impl(editor, input, |entry| { let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir()); if is_dir { @@ -314,8 +319,8 @@ pub mod completers { .collect() } - pub fn directory(_editor: &Editor, input: &str) -> Vec { - filename_impl(input, |entry| { + pub fn directory(editor: &Editor, input: &str) -> Vec { + filename_impl(editor, input, |entry| { let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir()); if is_dir { @@ -338,7 +343,7 @@ pub mod completers { } // TODO: we could return an iter/lazy thing so it can fetch as many as it needs. - fn filename_impl(input: &str, filter_fn: F) -> Vec + fn filename_impl(editor: &Editor, input: &str, filter_fn: F) -> Vec where F: Fn(&ignore::DirEntry) -> FileMatch, { @@ -370,6 +375,7 @@ pub mod completers { let mut files: Vec<_> = WalkBuilder::new(&dir) .hidden(false) + .follow_links(editor.config().file_picker.follow_symlinks) .max_depth(Some(1)) .build() .filter_map(|file| { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 8e53936a4..3bc3ecb1e 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -71,6 +71,9 @@ pub struct FilePickerConfig { /// Enables ignoring hidden files. /// Whether to hide hidden files in file picker and global search results. Defaults to true. pub hidden: bool, + /// Enables following symlinks. + /// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true. + pub follow_symlinks: bool, /// Enables reading ignore files from parent directories. Defaults to true. pub parents: bool, /// Enables reading `.ignore` files. @@ -94,6 +97,7 @@ impl Default for FilePickerConfig { fn default() -> Self { Self { hidden: true, + follow_symlinks: true, parents: true, ignore: true, git_ignore: true,