Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/mod.rs')
-rw-r--r--helix-term/src/ui/mod.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 2313fb80..92fa49dd 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -20,6 +20,7 @@ use crate::job::{self, Callback};
pub use completion::Completion;
pub use editor::EditorView;
use helix_stdx::rope;
+use helix_view::theme::Style;
pub use markdown::Markdown;
pub use menu::Menu;
pub use picker::{Column as PickerColumn, FileLocation, Picker};
@@ -29,7 +30,9 @@ pub use spinner::{ProgressSpinners, Spinner};
pub use text::Text;
use helix_view::Editor;
+use tui::text::Span;
+use std::path::Path;
use std::{error::Error, path::PathBuf};
struct Utf8PathBuf {
@@ -276,6 +279,74 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
picker
}
+type FileBrowser = Picker<(PathBuf, bool), (PathBuf, Style)>;
+
+pub fn file_browser(root: PathBuf, editor: &Editor) -> Result<FileBrowser, std::io::Error> {
+ let directory_style = editor.theme.get("ui.text.directory");
+ let directory_content = directory_content(&root)?;
+
+ let columns = [PickerColumn::new(
+ "path",
+ |(path, is_dir): &(PathBuf, bool), (root, directory_style): &(PathBuf, Style)| {
+ let name = path.strip_prefix(root).unwrap_or(path).to_string_lossy();
+ if *is_dir {
+ Span::styled(format!("{}/", name), *directory_style).into()
+ } else {
+ name.into()
+ }
+ },
+ )];
+ let picker = Picker::new(
+ columns,
+ 0,
+ directory_content,
+ (root, directory_style),
+ move |cx, (path, is_dir): &(PathBuf, bool), action| {
+ if *is_dir {
+ let new_root = helix_stdx::path::normalize(path);
+ let callback = Box::pin(async move {
+ let call: Callback =
+ Callback::EditorCompositor(Box::new(move |editor, compositor| {
+ if let Ok(picker) = file_browser(new_root, editor) {
+ compositor.push(Box::new(overlay::overlaid(picker)));
+ }
+ }));
+ Ok(call)
+ });
+ cx.jobs.callback(callback);
+ } else if let Err(e) = cx.editor.open(path, action) {
+ let err = if let Some(err) = e.source() {
+ format!("{}", err)
+ } else {
+ format!("unable to open \"{}\"", path.display())
+ };
+ cx.editor.set_error(err);
+ }
+ },
+ )
+ .with_preview(|_editor, (path, _is_dir)| Some((path.as_path().into(), None)));
+
+ Ok(picker)
+}
+
+fn directory_content(path: &Path) -> Result<Vec<(PathBuf, bool)>, std::io::Error> {
+ let mut content: Vec<_> = std::fs::read_dir(path)?
+ .flatten()
+ .map(|entry| {
+ (
+ entry.path(),
+ entry.file_type().is_ok_and(|file_type| file_type.is_dir()),
+ )
+ })
+ .collect();
+
+ content.sort_by(|(path1, is_dir1), (path2, is_dir2)| (!is_dir1, path1).cmp(&(!is_dir2, path2)));
+ if path.parent().is_some() {
+ content.insert(0, (path.join(".."), true));
+ }
+ Ok(content)
+}
+
pub mod completers {
use super::Utf8PathBuf;
use crate::ui::prompt::Completion;