on master help sign in
Add preview for scratch buffers in buffer picker (#3454)
2f9ca38A-Walrus
helix-term/src/commands.rs+3−3
@@ -2005,7 +2005,7 @@ fn global_search(cx: &mut Context) {
2005 align_view(doc, view, Align::Center);
2005 align_view(doc, view, Align::Center);
2006 },
2006 },
2007 |_editor, FileResult { path, line_num }| {
2007 |_editor, FileResult { path, line_num }| {
2008− Some((path.clone(), Some((*line_num, *line_num))))
2008+ Some((path.clone().into(), Some((*line_num, *line_num))))
2009 },
2009 },
2010 );
2010 );
2011 compositor.push(Box::new(overlayed(picker)));
2011 compositor.push(Box::new(overlayed(picker)));
@@ -2360,7 +2360,7 @@ fn buffer_picker(cx: &mut Context) {
2360 .selection(view_id)
2360 .selection(view_id)
2361 .primary()
2361 .primary()
2362 .cursor_line(doc.text().slice(..));
2362 .cursor_line(doc.text().slice(..));
2363− Some((meta.path.clone()?, Some((line, line))))
2363+ Some((meta.id.into(), Some((line, line))))
2364 },
2364 },
2365 );
2365 );
2366 cx.push_layer(Box::new(overlayed(picker)));
2366 cx.push_layer(Box::new(overlayed(picker)));
@@ -2441,7 +2441,7 @@ fn jumplist_picker(cx: &mut Context) {
2441 |editor, meta| {
2441 |editor, meta| {
2442 let doc = &editor.documents.get(&meta.id)?;
2442 let doc = &editor.documents.get(&meta.id)?;
2443 let line = meta.selection.primary().cursor_line(doc.text().slice(..));
2443 let line = meta.selection.primary().cursor_line(doc.text().slice(..));
2444− Some((meta.path.clone()?, Some((line, line))))
2444+ Some((meta.path.clone()?.into(), Some((line, line))))
2445 },
2445 },
2446 );
2446 );
2447 cx.push_layer(Box::new(overlayed(picker)));
2447 cx.push_layer(Box::new(overlayed(picker)));
helix-term/src/commands/dap.rs+2−2
@@ -85,7 +85,7 @@ fn thread_picker(
85 frame.line.saturating_sub(1),
85 frame.line.saturating_sub(1),
86 frame.end_line.unwrap_or(frame.line).saturating_sub(1),
86 frame.end_line.unwrap_or(frame.line).saturating_sub(1),
87 ));
87 ));
88− Some((path, pos))
88+ Some((path.into(), pos))
89 },
89 },
90 );
90 );
91 compositor.push(Box::new(picker));
91 compositor.push(Box::new(picker));
@@ -706,7 +706,7 @@ pub fn dap_switch_stack_frame(cx: &mut Context) {
706 .and_then(|source| source.path.clone())
706 .and_then(|source| source.path.clone())
707 .map(|path| {
707 .map(|path| {
708 (
708 (
709− path,
709+ path.into(),
710 Some((
710 Some((
711 frame.line.saturating_sub(1),
711 frame.line.saturating_sub(1),
712 frame.end_line.unwrap_or(frame.line).saturating_sub(1),
712 frame.end_line.unwrap_or(frame.line).saturating_sub(1),
helix-term/src/commands/lsp.rs+1−1
@@ -156,7 +156,7 @@ fn location_to_file_location(location: &lsp::Location) -> FileLocation {
156 location.range.start.line as usize,
156 location.range.start.line as usize,
157 location.range.end.line as usize,
157 location.range.end.line as usize,
158 ));
158 ));
159− (path, line)
159+ (path.into(), line)
160}
160}
161
161
162// TODO: share with symbol picker(symbol.location)
162// TODO: share with symbol picker(symbol.location)
helix-term/src/ui/mod.rs+1−1
@@ -230,7 +230,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
230 cx.editor.set_error(err);
230 cx.editor.set_error(err);
231 }
231 }
232 },
232 },
233− |_editor, path| Some((path.clone(), None)),
233+ |_editor, path| Some((path.clone().into(), None)),
234 )
234 )
235}
235}
236
236
helix-term/src/ui/picker.rs+81−48
@@ -12,18 +12,14 @@ use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
12use tui::widgets::Widget;
12use tui::widgets::Widget;
13
13
14use std::{cmp::Ordering, time::Instant};
14use std::{cmp::Ordering, time::Instant};
15−use std::{
16− collections::HashMap,
15+use std::{collections::HashMap, io::Read, path::PathBuf};
17− io::Read,
18− path::{Path, PathBuf},
19−};
20
16
21use crate::ui::{Prompt, PromptEvent};
17use crate::ui::{Prompt, PromptEvent};
22use helix_core::{movement::Direction, Position};
18use helix_core::{movement::Direction, Position};
23use helix_view::{
19use helix_view::{
24 editor::Action,
20 editor::Action,
25 graphics::{CursorKind, Margin, Modifier, Rect},
21 graphics::{CursorKind, Margin, Modifier, Rect},
26− Document, Editor,
22+ Document, DocumentId, Editor,
27};
23};
28
24
29use super::menu::Item;
25use super::menu::Item;
@@ -32,8 +28,36 @@ pub const MIN_AREA_WIDTH_FOR_PREVIEW: u16 = 72;
32/// Biggest file size to preview in bytes
28/// Biggest file size to preview in bytes
33pub const MAX_FILE_SIZE_FOR_PREVIEW: u64 = 10 * 1024 * 1024;
29pub const MAX_FILE_SIZE_FOR_PREVIEW: u64 = 10 * 1024 * 1024;
34
30
31+#[derive(PartialEq, Eq, Hash)]
32+pub enum PathOrId {
33+ Id(DocumentId),
34+ Path(PathBuf),
35+}
36+
37+impl PathOrId {
38+ fn get_canonicalized(self) -> std::io::Result<Self> {
39+ use PathOrId::*;
40+ Ok(match self {
41+ Path(path) => Path(helix_core::path::get_canonicalized_path(&path)?),
42+ Id(id) => Id(id),
43+ })
44+ }
45+}
46+
47+impl From<PathBuf> for PathOrId {
48+ fn from(v: PathBuf) -> Self {
49+ Self::Path(v)
50+ }
51+}
52+
53+impl From<DocumentId> for PathOrId {
54+ fn from(v: DocumentId) -> Self {
55+ Self::Id(v)
56+ }
57+}
58+
35/// File path and range of lines (used to align and highlight lines)
59/// File path and range of lines (used to align and highlight lines)
36−pub type FileLocation = (PathBuf, Option<(usize, usize)>);
60+pub type FileLocation = (PathOrId, Option<(usize, usize)>);
37
61
38pub struct FilePicker<T: Item> {
62pub struct FilePicker<T: Item> {
39 picker: Picker<T>,
63 picker: Picker<T>,
@@ -112,62 +136,71 @@ impl<T: Item> FilePicker<T> {
112 self.picker
136 self.picker
113 .selection()
137 .selection()
114 .and_then(|current| (self.file_fn)(editor, current))
138 .and_then(|current| (self.file_fn)(editor, current))
115− .and_then(|(path, line)| {
139+ .and_then(|(path_or_id, line)| path_or_id.get_canonicalized().ok().zip(Some(line)))
116− helix_core::path::get_canonicalized_path(&path)
117− .ok()
118− .zip(Some(line))
119− })
120 }
140 }
121
141
122 /// Get (cached) preview for a given path. If a document corresponding
142 /// Get (cached) preview for a given path. If a document corresponding
123 /// to the path is already open in the editor, it is used instead.
143 /// to the path is already open in the editor, it is used instead.
124 fn get_preview<'picker, 'editor>(
144 fn get_preview<'picker, 'editor>(
125 &'picker mut self,
145 &'picker mut self,
126− path: &Path,
146+ path_or_id: PathOrId,
127 editor: &'editor Editor,
147 editor: &'editor Editor,
128 ) -> Preview<'picker, 'editor> {
148 ) -> Preview<'picker, 'editor> {
149+ match path_or_id {
150+ PathOrId::Path(path) => {
151+ let path = &path;
129− if let Some(doc) = editor.document_by_path(path) {
152+ if let Some(doc) = editor.document_by_path(path) {
130− return Preview::EditorDocument(doc);
153+ return Preview::EditorDocument(doc);
131− }
154+ }
132
155
133− if self.preview_cache.contains_key(path) {
156+ if self.preview_cache.contains_key(path) {
134− return Preview::Cached(&self.preview_cache[path]);
157+ return Preview::Cached(&self.preview_cache[path]);
135− }
158+ }
136
159
137− let data = std::fs::File::open(path).and_then(|file| {
160+ let data = std::fs::File::open(path).and_then(|file| {
138− let metadata = file.metadata()?;
161+ let metadata = file.metadata()?;
139− // Read up to 1kb to detect the content type
162+ // Read up to 1kb to detect the content type
140− let n = file.take(1024).read_to_end(&mut self.read_buffer)?;
163+ let n = file.take(1024).read_to_end(&mut self.read_buffer)?;
141− let content_type = content_inspector::inspect(&self.read_buffer[..n]);
164+ let content_type = content_inspector::inspect(&self.read_buffer[..n]);
142− self.read_buffer.clear();
165+ self.read_buffer.clear();
143− Ok((metadata, content_type))
166+ Ok((metadata, content_type))
144− });
167+ });
145− let preview = data
168+ let preview = data
146− .map(
169+ .map(
147− |(metadata, content_type)| match (metadata.len(), content_type) {
170+ |(metadata, content_type)| match (metadata.len(), content_type) {
148− (_, content_inspector::ContentType::BINARY) => CachedPreview::Binary,
171+ (_, content_inspector::ContentType::BINARY) => CachedPreview::Binary,
149− (size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => CachedPreview::LargeFile,
172+ (size, _) if size > MAX_FILE_SIZE_FOR_PREVIEW => {
173+ CachedPreview::LargeFile
174+ }
150− _ => {
175+ _ => {
151− // TODO: enable syntax highlighting; blocked by async rendering
176+ // TODO: enable syntax highlighting; blocked by async rendering
152− Document::open(path, None, None)
177+ Document::open(path, None, None)
153− .map(|doc| CachedPreview::Document(Box::new(doc)))
178+ .map(|doc| CachedPreview::Document(Box::new(doc)))
154− .unwrap_or(CachedPreview::NotFound)
179+ .unwrap_or(CachedPreview::NotFound)
155− }
180+ }
156− },
181+ },
157− )
182+ )
158− .unwrap_or(CachedPreview::NotFound);
183+ .unwrap_or(CachedPreview::NotFound);
159− self.preview_cache.insert(path.to_owned(), preview);
184+ self.preview_cache.insert(path.to_owned(), preview);
160− Preview::Cached(&self.preview_cache[path])
185+ Preview::Cached(&self.preview_cache[path])
186+ }
187+ PathOrId::Id(id) => {
188+ let doc = editor.documents.get(&id).unwrap();
189+ Preview::EditorDocument(doc)
190+ }
191+ }
161 }
192 }
162
193
163 fn handle_idle_timeout(&mut self, cx: &mut Context) -> EventResult {
194 fn handle_idle_timeout(&mut self, cx: &mut Context) -> EventResult {
164 // Try to find a document in the cache
195 // Try to find a document in the cache
165 let doc = self
196 let doc = self
166 .current_file(cx.editor)
197 .current_file(cx.editor)
198+ .and_then(|(path, _range)| match path {
199+ PathOrId::Id(doc_id) => Some(doc_mut!(cx.editor, &doc_id)),
167− .and_then(|(path, _range)| self.preview_cache.get_mut(&path))
200+ PathOrId::Path(path) => match self.preview_cache.get_mut(&path) {
168− .and_then(|cache| match cache {
169− CachedPreview::Document(doc) => Some(doc),
201+ Some(CachedPreview::Document(doc)) => Some(doc),
170− _ => None,
202+ _ => None,
203+ },
171 });
204 });
172
205
173 // Then attempt to highlight it if it has no language set
206 // Then attempt to highlight it if it has no language set
@@ -224,7 +257,7 @@ impl<T: Item + 'static> Component for FilePicker<T> {
224 block.render(preview_area, surface);
257 block.render(preview_area, surface);
225
258
226 if let Some((path, range)) = self.current_file(cx.editor) {
259 if let Some((path, range)) = self.current_file(cx.editor) {
227− let preview = self.get_preview(&path, cx.editor);
260+ let preview = self.get_preview(path, cx.editor);
228 let doc = match preview.document() {
261 let doc = match preview.document() {
229 Some(doc) => doc,
262 Some(doc) => doc,
230 None => {
263 None => {