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.rs | 93 |
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; |