Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/picker.rs')
-rw-r--r--helix-term/src/ui/picker.rs93
1 files changed, 72 insertions, 21 deletions
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 9ff867c1..3986ad47 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -85,6 +85,7 @@ pub type FileLocation<'a> = (PathOrId<'a>, Option<(usize, usize)>);
pub enum CachedPreview {
Document(Box<Document>),
+ Directory(Vec<(String, bool)>),
Binary,
LargeFile,
NotFound,
@@ -106,12 +107,20 @@ impl Preview<'_, '_> {
}
}
+ fn dir_content(&self) -> Option<&Vec<(String, bool)>> {
+ match self {
+ Preview::Cached(CachedPreview::Directory(dir_content)) => Some(dir_content),
+ _ => None,
+ }
+ }
+
/// Alternate text to show for the preview.
fn placeholder(&self) -> &str {
match *self {
Self::EditorDocument(_) => "<Invalid file location>",
Self::Cached(preview) => match preview {
CachedPreview::Document(_) => "<Invalid file location>",
+ CachedPreview::Directory(_) => "<Invalid directory location>",
CachedPreview::Binary => "<Binary file>",
CachedPreview::LargeFile => "<File too large to preview>",
CachedPreview::NotFound => "<File not found>",
@@ -584,33 +593,58 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
}
let path: Arc<Path> = path.into();
- let data = std::fs::File::open(&path).and_then(|file| {
- let metadata = file.metadata()?;
- // Read up to 1kb to detect the content type
- let n = file.take(1024).read_to_end(&mut self.read_buffer)?;
- let content_type = content_inspector::inspect(&self.read_buffer[..n]);
- self.read_buffer.clear();
- Ok((metadata, content_type))
- });
- let preview = data
- .map(
- |(metadata, content_type)| match (metadata.len(), content_type) {
- (_, content_inspector::ContentType::BINARY) => CachedPreview::Binary,
- (size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => {
- CachedPreview::LargeFile
+ let preview = std::fs::metadata(&path)
+ .and_then(|metadata| {
+ if metadata.is_dir() {
+ let files = super::directory_content(&path)?;
+ let file_names: Vec<_> = files
+ .iter()
+ .filter_map(|(path, is_dir)| {
+ let name = path.file_name()?.to_string_lossy();
+ if *is_dir {
+ Some((format!("{}/", name), true))
+ } else {
+ Some((name.into_owned(), false))
+ }
+ })
+ .collect();
+ Ok(CachedPreview::Directory(file_names))
+ } else if metadata.is_file() {
+ if metadata.len() > MAX_FILE_SIZE_FOR_PREVIEW {
+ return Ok(CachedPreview::LargeFile);
+ }
+ let content_type = std::fs::File::open(&path).and_then(|file| {
+ // Read up to 1kb to detect the content type
+ let n = file.take(1024).read_to_end(&mut self.read_buffer)?;
+ let content_type =
+ content_inspector::inspect(&self.read_buffer[..n]);
+ self.read_buffer.clear();
+ Ok(content_type)
+ })?;
+ if content_type.is_binary() {
+ return Ok(CachedPreview::Binary);
}
- _ => Document::open(&path, None, None, editor.config.clone())
- .map(|doc| {
+ Document::open(&path, None, None, editor.config.clone()).map_or(
+ Err(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "Cannot open document",
+ )),
+ |doc| {
// Asynchronously highlight the new document
helix_event::send_blocking(
&self.preview_highlight_handler,
path.clone(),
);
- CachedPreview::Document(Box::new(doc))
- })
- .unwrap_or(CachedPreview::NotFound),
- },
- )
+ Ok(CachedPreview::Document(Box::new(doc)))
+ },
+ )
+ } else {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "Neither a dir, nor a file",
+ ))
+ }
+ })
.unwrap_or(CachedPreview::NotFound);
self.preview_cache.insert(path.clone(), preview);
Some((Preview::Cached(&self.preview_cache[&path]), range))
@@ -826,6 +860,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
// clear area
let background = cx.editor.theme.get("ui.background");
let text = cx.editor.theme.get("ui.text");
+ let directory = cx.editor.theme.get("ui.text.directory");
surface.clear_with(area, background);
const BLOCK: Block<'_> = Block::bordered();
@@ -847,6 +882,22 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
doc
}
_ => {
+ if let Some(dir_content) = preview.dir_content() {
+ for (i, (path, is_dir)) in
+ dir_content.iter().take(inner.height as usize).enumerate()
+ {
+ let style = if *is_dir { directory } else { text };
+ surface.set_stringn(
+ inner.x,
+ inner.y + i as u16,
+ path,
+ inner.width as usize,
+ style,
+ );
+ }
+ return;
+ }
+
let alt_text = preview.placeholder();
let x = inner.x + inner.width.saturating_sub(alt_text.len() as u16) / 2;
let y = inner.y + inner.height / 2;