Add preview for scratch buffers in buffer picker (#3454)

This commit is contained in:
A-Walrus 2022-11-21 03:58:35 +02:00 committed by GitHub
parent 420e33a600
commit 2f9ca3840a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 55 deletions

View file

@ -2005,7 +2005,7 @@ fn global_search(cx: &mut Context) {
align_view(doc, view, Align::Center); align_view(doc, view, Align::Center);
}, },
|_editor, FileResult { path, line_num }| { |_editor, FileResult { path, line_num }| {
Some((path.clone(), Some((*line_num, *line_num)))) Some((path.clone().into(), Some((*line_num, *line_num))))
}, },
); );
compositor.push(Box::new(overlayed(picker))); compositor.push(Box::new(overlayed(picker)));
@ -2360,7 +2360,7 @@ fn buffer_picker(cx: &mut Context) {
.selection(view_id) .selection(view_id)
.primary() .primary()
.cursor_line(doc.text().slice(..)); .cursor_line(doc.text().slice(..));
Some((meta.path.clone()?, Some((line, line)))) Some((meta.id.into(), Some((line, line))))
}, },
); );
cx.push_layer(Box::new(overlayed(picker))); cx.push_layer(Box::new(overlayed(picker)));
@ -2441,7 +2441,7 @@ fn jumplist_picker(cx: &mut Context) {
|editor, meta| { |editor, meta| {
let doc = &editor.documents.get(&meta.id)?; let doc = &editor.documents.get(&meta.id)?;
let line = meta.selection.primary().cursor_line(doc.text().slice(..)); let line = meta.selection.primary().cursor_line(doc.text().slice(..));
Some((meta.path.clone()?, Some((line, line)))) Some((meta.path.clone()?.into(), Some((line, line))))
}, },
); );
cx.push_layer(Box::new(overlayed(picker))); cx.push_layer(Box::new(overlayed(picker)));

View file

@ -85,7 +85,7 @@ fn thread_picker(
frame.line.saturating_sub(1), frame.line.saturating_sub(1),
frame.end_line.unwrap_or(frame.line).saturating_sub(1), frame.end_line.unwrap_or(frame.line).saturating_sub(1),
)); ));
Some((path, pos)) Some((path.into(), pos))
}, },
); );
compositor.push(Box::new(picker)); compositor.push(Box::new(picker));
@ -706,7 +706,7 @@ pub fn dap_switch_stack_frame(cx: &mut Context) {
.and_then(|source| source.path.clone()) .and_then(|source| source.path.clone())
.map(|path| { .map(|path| {
( (
path, path.into(),
Some(( Some((
frame.line.saturating_sub(1), frame.line.saturating_sub(1),
frame.end_line.unwrap_or(frame.line).saturating_sub(1), frame.end_line.unwrap_or(frame.line).saturating_sub(1),

View file

@ -156,7 +156,7 @@ fn location_to_file_location(location: &lsp::Location) -> FileLocation {
location.range.start.line as usize, location.range.start.line as usize,
location.range.end.line as usize, location.range.end.line as usize,
)); ));
(path, line) (path.into(), line)
} }
// TODO: share with symbol picker(symbol.location) // TODO: share with symbol picker(symbol.location)

View file

@ -230,7 +230,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
cx.editor.set_error(err); cx.editor.set_error(err);
} }
}, },
|_editor, path| Some((path.clone(), None)), |_editor, path| Some((path.clone().into(), None)),
) )
} }

View file

@ -12,18 +12,14 @@ use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
use tui::widgets::Widget; use tui::widgets::Widget;
use std::{cmp::Ordering, time::Instant}; use std::{cmp::Ordering, time::Instant};
use std::{ use std::{collections::HashMap, io::Read, path::PathBuf};
collections::HashMap,
io::Read,
path::{Path, PathBuf},
};
use crate::ui::{Prompt, PromptEvent}; use crate::ui::{Prompt, PromptEvent};
use helix_core::{movement::Direction, Position}; use helix_core::{movement::Direction, Position};
use helix_view::{ use helix_view::{
editor::Action, editor::Action,
graphics::{CursorKind, Margin, Modifier, Rect}, graphics::{CursorKind, Margin, Modifier, Rect},
Document, Editor, Document, DocumentId, Editor,
}; };
use super::menu::Item; use super::menu::Item;
@ -32,8 +28,36 @@ pub const MIN_AREA_WIDTH_FOR_PREVIEW: u16 = 72;
/// Biggest file size to preview in bytes /// Biggest file size to preview in bytes
pub const MAX_FILE_SIZE_FOR_PREVIEW: u64 = 10 * 1024 * 1024; pub const MAX_FILE_SIZE_FOR_PREVIEW: u64 = 10 * 1024 * 1024;
#[derive(PartialEq, Eq, Hash)]
pub enum PathOrId {
Id(DocumentId),
Path(PathBuf),
}
impl PathOrId {
fn get_canonicalized(self) -> std::io::Result<Self> {
use PathOrId::*;
Ok(match self {
Path(path) => Path(helix_core::path::get_canonicalized_path(&path)?),
Id(id) => Id(id),
})
}
}
impl From<PathBuf> for PathOrId {
fn from(v: PathBuf) -> Self {
Self::Path(v)
}
}
impl From<DocumentId> for PathOrId {
fn from(v: DocumentId) -> Self {
Self::Id(v)
}
}
/// File path and range of lines (used to align and highlight lines) /// File path and range of lines (used to align and highlight lines)
pub type FileLocation = (PathBuf, Option<(usize, usize)>); pub type FileLocation = (PathOrId, Option<(usize, usize)>);
pub struct FilePicker<T: Item> { pub struct FilePicker<T: Item> {
picker: Picker<T>, picker: Picker<T>,
@ -112,62 +136,71 @@ impl<T: Item> FilePicker<T> {
self.picker self.picker
.selection() .selection()
.and_then(|current| (self.file_fn)(editor, current)) .and_then(|current| (self.file_fn)(editor, current))
.and_then(|(path, line)| { .and_then(|(path_or_id, line)| path_or_id.get_canonicalized().ok().zip(Some(line)))
helix_core::path::get_canonicalized_path(&path)
.ok()
.zip(Some(line))
})
} }
/// Get (cached) preview for a given path. If a document corresponding /// Get (cached) preview for a given path. If a document corresponding
/// to the path is already open in the editor, it is used instead. /// to the path is already open in the editor, it is used instead.
fn get_preview<'picker, 'editor>( fn get_preview<'picker, 'editor>(
&'picker mut self, &'picker mut self,
path: &Path, path_or_id: PathOrId,
editor: &'editor Editor, editor: &'editor Editor,
) -> Preview<'picker, 'editor> { ) -> Preview<'picker, 'editor> {
if let Some(doc) = editor.document_by_path(path) { match path_or_id {
return Preview::EditorDocument(doc); PathOrId::Path(path) => {
} let path = &path;
if let Some(doc) = editor.document_by_path(path) {
return Preview::EditorDocument(doc);
}
if self.preview_cache.contains_key(path) { if self.preview_cache.contains_key(path) {
return Preview::Cached(&self.preview_cache[path]); return Preview::Cached(&self.preview_cache[path]);
} }
let data = std::fs::File::open(path).and_then(|file| { let data = std::fs::File::open(path).and_then(|file| {
let metadata = file.metadata()?; let metadata = file.metadata()?;
// Read up to 1kb to detect the content type // Read up to 1kb to detect the content type
let n = file.take(1024).read_to_end(&mut self.read_buffer)?; let n = file.take(1024).read_to_end(&mut self.read_buffer)?;
let content_type = content_inspector::inspect(&self.read_buffer[..n]); let content_type = content_inspector::inspect(&self.read_buffer[..n]);
self.read_buffer.clear(); self.read_buffer.clear();
Ok((metadata, content_type)) Ok((metadata, content_type))
}); });
let preview = data let preview = data
.map( .map(
|(metadata, content_type)| match (metadata.len(), content_type) { |(metadata, content_type)| match (metadata.len(), content_type) {
(_, content_inspector::ContentType::BINARY) => CachedPreview::Binary, (_, content_inspector::ContentType::BINARY) => CachedPreview::Binary,
(size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => CachedPreview::LargeFile, (size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => {
_ => { CachedPreview::LargeFile
// TODO: enable syntax highlighting; blocked by async rendering }
Document::open(path, None, None) _ => {
.map(|doc| CachedPreview::Document(Box::new(doc))) // TODO: enable syntax highlighting; blocked by async rendering
.unwrap_or(CachedPreview::NotFound) Document::open(path, None, None)
} .map(|doc| CachedPreview::Document(Box::new(doc)))
}, .unwrap_or(CachedPreview::NotFound)
) }
.unwrap_or(CachedPreview::NotFound); },
self.preview_cache.insert(path.to_owned(), preview); )
Preview::Cached(&self.preview_cache[path]) .unwrap_or(CachedPreview::NotFound);
self.preview_cache.insert(path.to_owned(), preview);
Preview::Cached(&self.preview_cache[path])
}
PathOrId::Id(id) => {
let doc = editor.documents.get(&id).unwrap();
Preview::EditorDocument(doc)
}
}
} }
fn handle_idle_timeout(&mut self, cx: &mut Context) -> EventResult { fn handle_idle_timeout(&mut self, cx: &mut Context) -> EventResult {
// Try to find a document in the cache // Try to find a document in the cache
let doc = self let doc = self
.current_file(cx.editor) .current_file(cx.editor)
.and_then(|(path, _range)| self.preview_cache.get_mut(&path)) .and_then(|(path, _range)| match path {
.and_then(|cache| match cache { PathOrId::Id(doc_id) => Some(doc_mut!(cx.editor, &doc_id)),
CachedPreview::Document(doc) => Some(doc), PathOrId::Path(path) => match self.preview_cache.get_mut(&path) {
_ => None, Some(CachedPreview::Document(doc)) => Some(doc),
_ => None,
},
}); });
// Then attempt to highlight it if it has no language set // Then attempt to highlight it if it has no language set
@ -224,7 +257,7 @@ impl<T: Item + 'static> Component for FilePicker<T> {
block.render(preview_area, surface); block.render(preview_area, surface);
if let Some((path, range)) = self.current_file(cx.editor) { if let Some((path, range)) = self.current_file(cx.editor) {
let preview = self.get_preview(&path, cx.editor); let preview = self.get_preview(path, cx.editor);
let doc = match preview.document() { let doc = match preview.document() {
Some(doc) => doc, Some(doc) => doc,
None => { None => {