on master help sign in
Make file preview callback optional
545acfdMichael Davis
helix-term/src/commands.rs+20−26
@@ -2184,11 +2184,9 @@ fn global_search(cx: &mut Context) {
2184
2184
2185 doc.set_selection(view.id, Selection::single(start, end));
2185 doc.set_selection(view.id, Selection::single(start, end));
2186 align_view(doc, view, Align::Center);
2186 align_view(doc, view, Align::Center);
2187− },
2188− |_editor, FileResult { path, line_num }| {
2187+ }).with_preview(|_editor, FileResult { path, line_num }| {
2189 Some((path.clone().into(), Some((*line_num, *line_num))))
2188 Some((path.clone().into(), Some((*line_num, *line_num))))
2190− },
2191− );
2189+ });
2192 compositor.push(Box::new(overlaid(picker)));
2190 compositor.push(Box::new(overlaid(picker)));
2193 },
2191 },
2194 ));
2192 ));
@@ -2579,22 +2577,18 @@ fn buffer_picker(cx: &mut Context) {
2579 // mru
2577 // mru
2580 items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));
2578 items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));
2581
2579
2582− let picker = FilePicker::new(
2580+ let picker = FilePicker::new(items, (), |cx, meta, action| {
2583− items,
2584− (),
2585− |cx, meta, action| {
2586− cx.editor.switch(meta.id, action);
2581+ cx.editor.switch(meta.id, action);
2587− },
2582+ })
2588− |editor, meta| {
2583+ .with_preview(|editor, meta| {
2589− let doc = &editor.documents.get(&meta.id)?;
2584+ let doc = &editor.documents.get(&meta.id)?;
2590− let &view_id = doc.selections().keys().next()?;
2585+ let &view_id = doc.selections().keys().next()?;
2591− let line = doc
2586+ let line = doc
2592− .selection(view_id)
2587+ .selection(view_id)
2593− .primary()
2588+ .primary()
2594− .cursor_line(doc.text().slice(..));
2589+ .cursor_line(doc.text().slice(..));
2595− Some((meta.id.into(), Some((line, line))))
2590+ Some((meta.id.into(), Some((line, line))))
2596− },
2597− );
2591+ });
2598 cx.push_layer(Box::new(overlaid(picker)));
2592 cx.push_layer(Box::new(overlaid(picker)));
2599}
2593}
2600
2594
@@ -2678,12 +2672,12 @@ fn jumplist_picker(cx: &mut Context) {
2678 doc.set_selection(view.id, meta.selection.clone());
2672 doc.set_selection(view.id, meta.selection.clone());
2679 view.ensure_cursor_in_view_center(doc, config.scrolloff);
2673 view.ensure_cursor_in_view_center(doc, config.scrolloff);
2680 },
2674 },
2675+ )
2681− |editor, meta| {
2676+ .with_preview(|editor, meta| {
2682− let doc = &editor.documents.get(&meta.id)?;
2677+ let doc = &editor.documents.get(&meta.id)?;
2683− let line = meta.selection.primary().cursor_line(doc.text().slice(..));
2678+ let line = meta.selection.primary().cursor_line(doc.text().slice(..));
2684− Some((meta.id.into(), Some((line, line))))
2679+ Some((meta.id.into(), Some((line, line))))
2685− },
2686− );
2680+ });
2687 cx.push_layer(Box::new(overlaid(picker)));
2681 cx.push_layer(Box::new(overlaid(picker)));
2688}
2682}
2689
2683
helix-term/src/commands/dap.rs+43−49
@@ -73,21 +73,19 @@ fn thread_picker(
73 let debugger = debugger!(editor);
73 let debugger = debugger!(editor);
74
74
75 let thread_states = debugger.thread_states.clone();
75 let thread_states = debugger.thread_states.clone();
76− let picker = FilePicker::new(
76+ let picker = FilePicker::new(threads, thread_states, move |cx, thread, _action| {
77− threads,
78− thread_states,
79− move |cx, thread, _action| callback_fn(cx.editor, thread),
77+ callback_fn(cx.editor, thread)
78+ })
80− move |editor, thread| {
79+ .with_preview(move |editor, thread| {
81− let frames = editor.debugger.as_ref()?.stack_frames.get(&thread.id)?;
80+ let frames = editor.debugger.as_ref()?.stack_frames.get(&thread.id)?;
82− let frame = frames.get(0)?;
81+ let frame = frames.get(0)?;
83− let path = frame.source.as_ref()?.path.clone()?;
82+ let path = frame.source.as_ref()?.path.clone()?;
84− let pos = Some((
83+ let pos = Some((
85− frame.line.saturating_sub(1),
84+ frame.line.saturating_sub(1),
86− frame.end_line.unwrap_or(frame.line).saturating_sub(1),
85+ frame.end_line.unwrap_or(frame.line).saturating_sub(1),
87− ));
86+ ));
88− Some((path.into(), pos))
87+ Some((path.into(), pos))
89− },
90− );
88+ });
91 compositor.push(Box::new(picker));
89 compositor.push(Box::new(picker));
92 },
90 },
93 );
91 );
@@ -728,39 +726,35 @@ pub fn dap_switch_stack_frame(cx: &mut Context) {
728
726
729 let frames = debugger.stack_frames[&thread_id].clone();
727 let frames = debugger.stack_frames[&thread_id].clone();
730
728
731− let picker = FilePicker::new(
729+ let picker = FilePicker::new(frames, (), move |cx, frame, _action| {
732− frames,
733− (),
734− move |cx, frame, _action| {
735− let debugger = debugger!(cx.editor);
730+ let debugger = debugger!(cx.editor);
736− // TODO: this should be simpler to find
731+ // TODO: this should be simpler to find
737− let pos = debugger.stack_frames[&thread_id]
732+ let pos = debugger.stack_frames[&thread_id]
738− .iter()
733+ .iter()
739− .position(|f| f.id == frame.id);
734+ .position(|f| f.id == frame.id);
740− debugger.active_frame = pos;
735+ debugger.active_frame = pos;
741−
736+
742− let frame = debugger.stack_frames[&thread_id]
737+ let frame = debugger.stack_frames[&thread_id]
743− .get(pos.unwrap_or(0))
738+ .get(pos.unwrap_or(0))
744− .cloned();
739+ .cloned();
745− if let Some(frame) = &frame {
740+ if let Some(frame) = &frame {
746− jump_to_stack_frame(cx.editor, frame);
741+ jump_to_stack_frame(cx.editor, frame);
747− }
742+ }
748− },
743+ })
749− move |_editor, frame| {
744+ .with_preview(move |_editor, frame| {
750− frame
745+ frame
751− .source
746+ .source
752− .as_ref()
747+ .as_ref()
753− .and_then(|source| source.path.clone())
748+ .and_then(|source| source.path.clone())
754− .map(|path| {
749+ .map(|path| {
755− (
750+ (
756− path.into(),
751+ path.into(),
757− Some((
752+ Some((
758− frame.line.saturating_sub(1),
753+ frame.line.saturating_sub(1),
759− frame.end_line.unwrap_or(frame.line).saturating_sub(1),
754+ frame.end_line.unwrap_or(frame.line).saturating_sub(1),
760− )),
755+ )),
761− )
762− })
756+ )
763− },
757+ })
764− );
758+ });
765 cx.push_layer(Box::new(picker))
759 cx.push_layer(Box::new(picker))
766}
760}
helix-term/src/commands/lsp.rs+36−44
@@ -240,44 +240,40 @@ type SymbolPicker = FilePicker<SymbolInformationItem>;
240
240
241fn sym_picker(symbols: Vec<SymbolInformationItem>, current_path: Option<lsp::Url>) -> SymbolPicker {
241fn sym_picker(symbols: Vec<SymbolInformationItem>, current_path: Option<lsp::Url>) -> SymbolPicker {
242 // TODO: drop current_path comparison and instead use workspace: bool flag?
242 // TODO: drop current_path comparison and instead use workspace: bool flag?
243− FilePicker::new(
244− symbols,
245− current_path.clone(),
243+ FilePicker::new(symbols, current_path.clone(), move |cx, item, action| {
246− move |cx, item, action| {
247− let (view, doc) = current!(cx.editor);
244+ let (view, doc) = current!(cx.editor);
248− push_jump(view, doc);
245+ push_jump(view, doc);
249−
246+
250− if current_path.as_ref() != Some(&item.symbol.location.uri) {
247+ if current_path.as_ref() != Some(&item.symbol.location.uri) {
251− let uri = &item.symbol.location.uri;
248+ let uri = &item.symbol.location.uri;
252− let path = match uri.to_file_path() {
249+ let path = match uri.to_file_path() {
253− Ok(path) => path,
250+ Ok(path) => path,
254− Err(_) => {
251+ Err(_) => {
255− let err = format!("unable to convert URI to filepath: {}", uri);
252+ let err = format!("unable to convert URI to filepath: {}", uri);
256− cx.editor.set_error(err);
257− return;
258− }
259− };
260− if let Err(err) = cx.editor.open(&path, action) {
261− let err = format!("failed to open document: {}: {}", uri, err);
262− log::error!("{}", err);
263 cx.editor.set_error(err);
253 cx.editor.set_error(err);
264 return;
254 return;
265 }
255 }
256+ };
257+ if let Err(err) = cx.editor.open(&path, action) {
258+ let err = format!("failed to open document: {}: {}", uri, err);
259+ log::error!("{}", err);
260+ cx.editor.set_error(err);
261+ return;
266 }
262 }
263+ }
267
264
268− let (view, doc) = current!(cx.editor);
265+ let (view, doc) = current!(cx.editor);
269
266
270− if let Some(range) =
267+ if let Some(range) =
271− lsp_range_to_range(doc.text(), item.symbol.location.range, item.offset_encoding)
268+ lsp_range_to_range(doc.text(), item.symbol.location.range, item.offset_encoding)
272− {
269+ {
273− // we flip the range so that the cursor sits on the start of the symbol
270+ // we flip the range so that the cursor sits on the start of the symbol
274− // (for example start of the function).
271+ // (for example start of the function).
275− doc.set_selection(view.id, Selection::single(range.head, range.anchor));
272+ doc.set_selection(view.id, Selection::single(range.head, range.anchor));
276− align_view(doc, view, Align::Center);
273+ align_view(doc, view, Align::Center);
277− }
274+ }
278− },
275+ })
279− move |_editor, item| Some(location_to_file_location(&item.symbol.location)),
276+ .with_preview(move |_editor, item| Some(location_to_file_location(&item.symbol.location)))
280− )
281 .truncate_start(false)
277 .truncate_start(false)
282}
278}
283
279
@@ -345,11 +341,11 @@ fn diag_picker(
345 align_view(doc, view, Align::Center);
341 align_view(doc, view, Align::Center);
346 }
342 }
347 },
343 },
348− move |_editor, PickerDiagnostic { url, diag, .. }| {
349− let location = lsp::Location::new(url.clone(), diag.range);
350− Some(location_to_file_location(&location))
351− },
352 )
344 )
345+ .with_preview(move |_editor, PickerDiagnostic { url, diag, .. }| {
346+ let location = lsp::Location::new(url.clone(), diag.range);
347+ Some(location_to_file_location(&location))
348+ })
353 .truncate_start(false)
349 .truncate_start(false)
354}
350}
355
351
@@ -1047,14 +1043,10 @@ fn goto_impl(
1047 editor.set_error("No definition found.");
1043 editor.set_error("No definition found.");
1048 }
1044 }
1049 _locations => {
1045 _locations => {
1050− let picker = FilePicker::new(
1046+ let picker = FilePicker::new(locations, cwdir, move |cx, location, action| {
1051− locations,
1052− cwdir,
1053− move |cx, location, action| {
1054− jump_to_location(cx.editor, location, offset_encoding, action)
1047+ jump_to_location(cx.editor, location, offset_encoding, action)
1055− },
1048+ })
1056− move |_editor, location| Some(location_to_file_location(location)),
1049+ .with_preview(move |_editor, location| Some(location_to_file_location(location)));
1057− );
1058 compositor.push(Box::new(overlaid(picker)));
1050 compositor.push(Box::new(overlaid(picker)));
1059 }
1051 }
1060 }
1052 }
helix-term/src/ui/mod.rs+11−15
@@ -217,21 +217,17 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
217
217
218 log::debug!("file_picker init {:?}", Instant::now().duration_since(now));
218 log::debug!("file_picker init {:?}", Instant::now().duration_since(now));
219
219
220− FilePicker::new(
221− files,
222− root,
223− move |cx, path: &PathBuf, action| {
220+ FilePicker::new(files, root, move |cx, path: &PathBuf, action| {
224− if let Err(e) = cx.editor.open(path, action) {
221+ if let Err(e) = cx.editor.open(path, action) {
225− let err = if let Some(err) = e.source() {
222+ let err = if let Some(err) = e.source() {
226− format!("{}", err)
223+ format!("{}", err)
227− } else {
224+ } else {
228− format!("unable to open \"{}\"", path.display())
225+ format!("unable to open \"{}\"", path.display())
229− };
226+ };
230− cx.editor.set_error(err);
227+ cx.editor.set_error(err);
231− }
228+ }
232− },
229+ })
233− |_editor, path| Some((path.clone().into(), None)),
230+ .with_preview(|_editor, path| Some((path.clone().into(), None)))
234− )
235}
231}
236
232
237pub mod completers {
233pub mod completers {
helix-term/src/ui/picker.rs+11−4
@@ -141,7 +141,7 @@ pub struct FilePicker<T: Item> {
141 preview_cache: HashMap<PathBuf, CachedPreview>,
141 preview_cache: HashMap<PathBuf, CachedPreview>,
142 read_buffer: Vec<u8>,
142 read_buffer: Vec<u8>,
143 /// Given an item in the picker, return the file path and line number to display.
143 /// Given an item in the picker, return the file path and line number to display.
144− file_fn: FileCallback<T>,
144+ file_fn: Option<FileCallback<T>>,
145}
145}
146
146
147impl<T: Item + 'static> FilePicker<T> {
147impl<T: Item + 'static> FilePicker<T> {
@@ -149,7 +149,6 @@ impl<T: Item + 'static> FilePicker<T> {
149 options: Vec<T>,
149 options: Vec<T>,
150 editor_data: T::Data,
150 editor_data: T::Data,
151 callback_fn: impl Fn(&mut Context, &T, Action) + 'static,
151 callback_fn: impl Fn(&mut Context, &T, Action) + 'static,
152− preview_fn: impl Fn(&Editor, &T) -> Option<FileLocation> + 'static,
153 ) -> Self {
152 ) -> Self {
154 let prompt = Prompt::new(
153 let prompt = Prompt::new(
155 "".into(),
154 "".into(),
@@ -173,7 +172,7 @@ impl<T: Item + 'static> FilePicker<T> {
173 widths: Vec::new(),
172 widths: Vec::new(),
174 preview_cache: HashMap::new(),
173 preview_cache: HashMap::new(),
175 read_buffer: Vec::with_capacity(1024),
174 read_buffer: Vec::with_capacity(1024),
176− file_fn: Box::new(preview_fn),
175+ file_fn: None,
177
176
178 picker: unimplemented!(),
177 picker: unimplemented!(),
179 };
178 };
@@ -202,6 +201,14 @@ impl<T: Item + 'static> FilePicker<T> {
202 self
201 self
203 }
202 }
204
203
204+ pub fn with_preview(
205+ mut self,
206+ preview_fn: impl Fn(&Editor, &T) -> Option<FileLocation> + 'static,
207+ ) -> Self {
208+ self.file_fn = Some(Box::new(preview_fn));
209+ self
210+ }
211+
205 pub fn set_options(&mut self, new_options: Vec<T>) {
212 pub fn set_options(&mut self, new_options: Vec<T>) {
206 self.options = new_options;
213 self.options = new_options;
207 self.cursor = 0;
214 self.cursor = 0;
@@ -372,7 +379,7 @@ impl<T: Item + 'static> FilePicker<T> {
372 fn current_file(&self, editor: &Editor) -> Option<FileLocation> {
379 fn current_file(&self, editor: &Editor) -> Option<FileLocation> {
373 self.picker
380 self.picker
374 .selection()
381 .selection()
375− .and_then(|current| (self.file_fn)(editor, current))
382+ .and_then(|current| (self.file_fn.as_ref()?)(editor, current))
376 .and_then(|(path_or_id, line)| path_or_id.get_canonicalized().ok().zip(Some(line)))
383 .and_then(|(path_or_id, line)| path_or_id.get_canonicalized().ok().zip(Some(line)))
377 }
384 }
378
385