on master help sign in
Better handling of symlinks (#2718)
c2cc203Roland Kovacs
helix-term/src/ui/mod.rs+19−13
@@ -118,6 +118,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
118 .hidden(config.file_picker.hidden)
118 .hidden(config.file_picker.hidden)
119 .parents(config.file_picker.parents)
119 .parents(config.file_picker.parents)
120 .ignore(config.file_picker.ignore)
120 .ignore(config.file_picker.ignore)
121+ .follow_links(config.file_picker.follow_symlinks)
121 .git_ignore(config.file_picker.git_ignore)
122 .git_ignore(config.file_picker.git_ignore)
122 .git_global(config.file_picker.git_global)
123 .git_global(config.file_picker.git_global)
123 .git_exclude(config.file_picker.git_exclude)
124 .git_exclude(config.file_picker.git_exclude)
@@ -146,14 +147,13 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
146 let entry = entry.ok()?;
147 let entry = entry.ok()?;
147
148
148 // This is faster than entry.path().is_dir() since it uses cached fs::Metadata fetched by ignore/walkdir
149 // 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+ let is_dir = entry.file_type().map_or(false, |ft| ft.is_dir());
150−
151 if is_dir {
151 if is_dir {
152 // Will give a false positive if metadata cannot be read (eg. permission error)
152 // 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())
154 }
156 }
155−
156− Some(entry.into_path())
157 });
157 });
158
158
159 // Cap the number of files if we aren't in a git project, preventing
159 // 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
175 path.strip_prefix(&root).unwrap_or(path).to_string_lossy()
175 path.strip_prefix(&root).unwrap_or(path).to_string_lossy()
176 },
176 },
177 move |cx, path: &PathBuf, action| {
177 move |cx, path: &PathBuf, action| {
178− cx.editor
179− .open(path.into(), action)
178+ if let Err(e) = cx.editor.open(path.into(), action) {
180− .expect("editor.open failed");
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+ }
181 },
186 },
182 |_editor, path| Some((path.clone(), None)),
187 |_editor, path| Some((path.clone(), None)),
183 )
188 )
@@ -281,8 +286,8 @@ pub mod completers {
281 .collect()
286 .collect()
282 }
287 }
283
288
284− pub fn filename(_editor: &Editor, input: &str) -> Vec<Completion> {
289+ pub fn filename(editor: &Editor, input: &str) -> Vec<Completion> {
285− filename_impl(input, |entry| {
290+ filename_impl(editor, input, |entry| {
286 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
291 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
287
292
288 if is_dir {
293 if is_dir {
@@ -314,8 +319,8 @@ pub mod completers {
314 .collect()
319 .collect()
315 }
320 }
316
321
317− pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
322+ pub fn directory(editor: &Editor, input: &str) -> Vec<Completion> {
318− filename_impl(input, |entry| {
323+ filename_impl(editor, input, |entry| {
319 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
324 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
320
325
321 if is_dir {
326 if is_dir {
@@ -338,7 +343,7 @@ pub mod completers {
338 }
343 }
339
344
340 // TODO: we could return an iter/lazy thing so it can fetch as many as it needs.
345 // 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>
342 where
347 where
343 F: Fn(&ignore::DirEntry) -> FileMatch,
348 F: Fn(&ignore::DirEntry) -> FileMatch,
344 {
349 {
@@ -370,6 +375,7 @@ pub mod completers {
370
375
371 let mut files: Vec<_> = WalkBuilder::new(&dir)
376 let mut files: Vec<_> = WalkBuilder::new(&dir)
372 .hidden(false)
377 .hidden(false)
378+ .follow_links(editor.config().file_picker.follow_symlinks)
373 .max_depth(Some(1))
379 .max_depth(Some(1))
374 .build()
380 .build()
375 .filter_map(|file| {
381 .filter_map(|file| {
helix-view/src/editor.rs+4
@@ -71,6 +71,9 @@ pub struct FilePickerConfig {
71 /// Enables ignoring hidden files.
71 /// Enables ignoring hidden files.
72 /// Whether to hide hidden files in file picker and global search results. Defaults to true.
72 /// Whether to hide hidden files in file picker and global search results. Defaults to true.
73 pub hidden: bool,
73 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,
74 /// Enables reading ignore files from parent directories. Defaults to true.
77 /// Enables reading ignore files from parent directories. Defaults to true.
75 pub parents: bool,
78 pub parents: bool,
76 /// Enables reading `.ignore` files.
79 /// Enables reading `.ignore` files.
@@ -94,6 +97,7 @@ impl Default for FilePickerConfig {
94 fn default() -> Self {
97 fn default() -> Self {
95 Self {
98 Self {
96 hidden: true,
99 hidden: true,
100+ follow_symlinks: true,
97 parents: true,
101 parents: true,
98 ignore: true,
102 ignore: true,
99 git_ignore: true,
103 git_ignore: true,