on master help sign in
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

commitc2cc2037b548a39d374b11c6919e10b1a7115cfb authorRoland Kovacs <zen3ger@tutanota.com> dateWed 15 Jun 2022, 06:17 (4 years ago) parent7983c71
2 files changed+23−13
helix-term/src/ui/mod.rs+19−13 helix-view/src/editor.rs+4
helix-term/src/ui/mod.rs+19−13
@@ -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
@@ -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,