Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide file-picker navigation #6116

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ Keys to use within picker. Remapping currently not supported.
| `Ctrl-v` | Open vertically |
| `Ctrl-t` | Toggle preview |
| `Escape`, `Ctrl-c` | Close picker |
| `Ctrl-e` | Change picker root to one directory in, based on selection |
| `Ctrl-a` | Change picker root to one directory out |

## Prompt

Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl Application {

// If the first file is a directory, skip it and open a picker
if let Some((first, _)) = files_it.next_if(|(p, _)| p.is_dir()) {
let picker = ui::file_picker(first, &config.load().editor);
let picker = ui::file_picker(first, editor.config().file_picker);
compositor.push(Box::new(overlaid(picker)));
}

Expand Down
21 changes: 14 additions & 7 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ fn goto_file_impl(cx: &mut Context, action: Action) {

let path = &rel_path.join(p);
if path.is_dir() {
let picker = ui::file_picker(path.into(), &cx.editor.config());
let picker = ui::file_picker(path.into(), cx.editor.config().file_picker);
cx.push_layer(Box::new(overlaid(picker)));
} else if let Err(e) = cx.editor.open(path, action) {
cx.editor.set_error(format!("Open file failed: {:?}", e));
Expand Down Expand Up @@ -1266,7 +1266,7 @@ fn open_url(cx: &mut Context, url: Url, action: Action) {
Ok(_) | Err(_) => {
let path = &rel_path.join(url.path());
if path.is_dir() {
let picker = ui::file_picker(path.into(), &cx.editor.config());
let picker = ui::file_picker(path.into(), cx.editor.config().file_picker);
cx.push_layer(Box::new(overlaid(picker)));
} else if let Err(e) = cx.editor.open(path, action) {
cx.editor.set_error(format!("Open file failed: {:?}", e));
Expand Down Expand Up @@ -2221,6 +2221,7 @@ fn global_search(cx: &mut Context) {

impl ui::menu::Item for FileResult {
type Data = Option<PathBuf>;
type Config = ();

fn format(&self, current_path: &Self::Data) -> Row {
let relative_path = helix_stdx::path::get_relative_path(&self.path)
Expand Down Expand Up @@ -2810,7 +2811,7 @@ fn file_picker(cx: &mut Context) {
cx.editor.set_error("Workspace directory does not exist");
return;
}
let picker = ui::file_picker(root, &cx.editor.config());
let picker = ui::file_picker(root, cx.editor.config().file_picker);
cx.push_layer(Box::new(overlaid(picker)));
}

Expand All @@ -2827,7 +2828,7 @@ fn file_picker_in_current_buffer_directory(cx: &mut Context) {
}
};

let picker = ui::file_picker(path, &cx.editor.config());
let picker = ui::file_picker(path, cx.editor.config().file_picker);
cx.push_layer(Box::new(overlaid(picker)));
}

Expand All @@ -2838,7 +2839,7 @@ fn file_picker_in_current_directory(cx: &mut Context) {
.set_error("Current working directory does not exist");
return;
}
let picker = ui::file_picker(cwd, &cx.editor.config());
let picker = ui::file_picker(cwd, cx.editor.config().file_picker);
cx.push_layer(Box::new(overlaid(picker)));
}

Expand All @@ -2855,6 +2856,7 @@ fn buffer_picker(cx: &mut Context) {

impl ui::menu::Item for BufferMeta {
type Data = ();
type Config = ();

fn format(&self, _data: &Self::Data) -> Row {
let path = self
Expand Down Expand Up @@ -2896,7 +2898,7 @@ fn buffer_picker(cx: &mut Context) {
// mru
items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at));

let picker = Picker::new(items, (), |cx, meta, action| {
let picker = Picker::new((), items, (), |cx, meta, action| {
cx.editor.switch(meta.id, action);
})
.with_preview(|editor, meta| {
Expand All @@ -2922,6 +2924,7 @@ fn jumplist_picker(cx: &mut Context) {

impl ui::menu::Item for JumpMeta {
type Data = ();
type Config = ();

fn format(&self, _data: &Self::Data) -> Row {
let path = self
Expand Down Expand Up @@ -2974,6 +2977,7 @@ fn jumplist_picker(cx: &mut Context) {
};

let picker = Picker::new(
(),
cx.editor
.tree
.views()
Expand Down Expand Up @@ -3014,6 +3018,7 @@ fn changed_file_picker(cx: &mut Context) {

impl Item for FileChange {
type Data = FileChangeData;
type Config = ();

fn format(&self, data: &Self::Data) -> Row {
let process_path = |path: &PathBuf| {
Expand Down Expand Up @@ -3053,6 +3058,7 @@ fn changed_file_picker(cx: &mut Context) {
let renamed = cx.editor.theme.get("diff.delta.moved");

let picker = Picker::new(
(),
Vec::new(),
FileChangeData {
cwd: cwd.clone(),
Expand Down Expand Up @@ -3092,6 +3098,7 @@ fn changed_file_picker(cx: &mut Context) {

impl ui::menu::Item for MappableCommand {
type Data = ReverseKeymap;
type Config = ();

fn format(&self, keymap: &Self::Data) -> Row {
let fmt_binding = |bindings: &Vec<Vec<KeyEvent>>| -> String {
Expand Down Expand Up @@ -3138,7 +3145,7 @@ pub fn command_palette(cx: &mut Context) {
}
}));

let picker = Picker::new(commands, keymap, move |cx, command, _action| {
let picker = Picker::new((), commands, keymap, move |cx, command, _action| {
let mut ctx = Context {
register,
count,
Expand Down
8 changes: 6 additions & 2 deletions helix-term/src/commands/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use helix_view::handlers::dap::{breakpoints_changed, jump_to_stack_frame, select

impl ui::menu::Item for StackFrame {
type Data = ();
type Config = ();

fn format(&self, _data: &Self::Data) -> Row {
self.name.as_str().into() // TODO: include thread_states in the label
Expand All @@ -32,6 +33,7 @@ impl ui::menu::Item for StackFrame {

impl ui::menu::Item for DebugTemplate {
type Data = ();
type Config = ();

fn format(&self, _data: &Self::Data) -> Row {
self.name.as_str().into()
Expand All @@ -40,6 +42,7 @@ impl ui::menu::Item for DebugTemplate {

impl ui::menu::Item for Thread {
type Data = ThreadStates;
type Config = ();

fn format(&self, thread_states: &Self::Data) -> Row {
format!(
Expand Down Expand Up @@ -73,7 +76,7 @@ fn thread_picker(
let debugger = debugger!(editor);

let thread_states = debugger.thread_states.clone();
let picker = Picker::new(threads, thread_states, move |cx, thread, _action| {
let picker = Picker::new((), threads, thread_states, move |cx, thread, _action| {
callback_fn(cx.editor, thread)
})
.with_preview(move |editor, thread| {
Expand Down Expand Up @@ -269,6 +272,7 @@ pub fn dap_launch(cx: &mut Context) {
let templates = config.templates.clone();

cx.push_layer(Box::new(overlaid(Picker::new(
(),
templates,
(),
|cx, template, _action| {
Expand Down Expand Up @@ -735,7 +739,7 @@ pub fn dap_switch_stack_frame(cx: &mut Context) {

let frames = debugger.stack_frames[&thread_id].clone();

let picker = Picker::new(frames, (), move |cx, frame, _action| {
let picker = Picker::new((), frames, (), move |cx, frame, _action| {
let debugger = debugger!(cx.editor);
// TODO: this should be simpler to find
let pos = debugger.stack_frames[&thread_id]
Expand Down
10 changes: 8 additions & 2 deletions helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ macro_rules! language_server_with_feature {
impl ui::menu::Item for lsp::Location {
/// Current working directory.
type Data = PathBuf;
type Config = ();

fn format(&self, cwdir: &Self::Data) -> Row {
// The preallocation here will overallocate a few characters since it will account for the
Expand Down Expand Up @@ -105,6 +106,7 @@ struct SymbolInformationItem {
impl ui::menu::Item for SymbolInformationItem {
/// Path to currently focussed document
type Data = Option<lsp::Url>;
type Config = ();

fn format(&self, current_doc_path: &Self::Data) -> Row {
if current_doc_path.as_ref() == Some(&self.symbol.location.uri) {
Expand Down Expand Up @@ -141,6 +143,7 @@ struct PickerDiagnostic {

impl ui::menu::Item for PickerDiagnostic {
type Data = (DiagnosticStyles, DiagnosticsFormat);
type Config = ();

fn format(&self, (styles, format): &Self::Data) -> Row {
let mut style = self
Expand Down Expand Up @@ -246,7 +249,7 @@ type SymbolPicker = Picker<SymbolInformationItem>;

fn sym_picker(symbols: Vec<SymbolInformationItem>, current_path: Option<lsp::Url>) -> SymbolPicker {
// TODO: drop current_path comparison and instead use workspace: bool flag?
Picker::new(symbols, current_path, move |cx, item, action| {
Picker::new((), symbols, current_path, move |cx, item, action| {
jump_to_location(
cx.editor,
&item.symbol.location,
Expand Down Expand Up @@ -295,6 +298,7 @@ fn diag_picker(
};

Picker::new(
(),
flat_diag,
(styles, format),
move |cx,
Expand Down Expand Up @@ -502,6 +506,7 @@ struct CodeActionOrCommandItem {

impl ui::menu::Item for CodeActionOrCommandItem {
type Data = ();
type Config = ();
fn format(&self, _data: &Self::Data) -> Row {
match &self.lsp_item {
lsp::CodeActionOrCommand::CodeAction(action) => action.title.as_str().into(),
Expand Down Expand Up @@ -752,6 +757,7 @@ pub fn code_action(cx: &mut Context) {

impl ui::menu::Item for lsp::Command {
type Data = ();
type Config = ();
fn format(&self, _data: &Self::Data) -> Row {
self.title.as_str().into()
}
Expand Down Expand Up @@ -822,7 +828,7 @@ fn goto_impl(
}
[] => unreachable!("`locations` should be non-empty for `goto_impl`"),
_locations => {
let picker = Picker::new(locations, cwdir, move |cx, location, action| {
let picker = Picker::new((), locations, cwdir, move |cx, location, action| {
jump_to_location(cx.editor, location, offset_encoding, action)
})
.with_preview(move |_editor, location| Some(location_to_file_location(location)));
Expand Down
5 changes: 3 additions & 2 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ fn open(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
let callback = async move {
let call: job::Callback = job::Callback::EditorCompositor(Box::new(
move |editor: &mut Editor, compositor: &mut Compositor| {
let picker = ui::file_picker(path.into_owned(), &editor.config());
let picker =
ui::file_picker(path.into_owned(), editor.config().file_picker);
compositor.push(Box::new(overlaid(picker)));
},
));
Expand Down Expand Up @@ -1395,7 +1396,7 @@ fn lsp_workspace_command(
let callback = async move {
let call: job::Callback = Callback::EditorCompositor(Box::new(
move |_editor: &mut Editor, compositor: &mut Compositor| {
let picker = ui::Picker::new(commands, (), move |cx, command, _action| {
let picker = ui::Picker::new((), commands, (), move |cx, command, _action| {
execute_lsp_command(cx.editor, language_server_id, command.clone());
});
compositor.push(Box::new(overlaid(picker)))
Expand Down
1 change: 1 addition & 0 deletions helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use helix_lsp::{lsp, util, OffsetEncoding};

impl menu::Item for CompletionItem {
type Data = ();
type Config = ();
fn sort_text(&self, data: &Self::Data) -> Cow<str> {
self.filter_text(data)
}
Expand Down
19 changes: 16 additions & 3 deletions helix-term/src/ui/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ use tui::{
pub use tui::widgets::{Cell, Row};

use helix_view::{
editor::SmartTabConfig,
editor::{FilePickerConfig, SmartTabConfig},
graphics::{Margin, Rect},
Editor,
};
use tui::layout::Constraint;

pub trait Item: Sync + Send + 'static {
pub trait Item: Sync + Send + Sized + 'static {
/// Additional editor state that is used for label calculation.
type Data: Sync + Send + 'static;
type Config;

fn format(&self, data: &Self::Data) -> Row;

Expand All @@ -36,18 +37,30 @@ pub trait Item: Sync + Send + 'static {
let label: String = self.format(data).cell_text().collect();
label.into()
}

fn goto_parent(_picker: &mut Picker<Self>, _cx: &mut Context) {}
fn goto_child(_picker: &mut Picker<Self>, _cx: &mut Context) {}
}

impl Item for PathBuf {
/// Root prefix to strip.
type Data = PathBuf;
type Config = FilePickerConfig;

fn format(&self, root_path: &Self::Data) -> Row {
self.strip_prefix(root_path)
.unwrap_or(self)
.to_string_lossy()
.into()
}

fn goto_parent(picker: &mut Picker<PathBuf>, cx: &mut Context) {
picker.goto_parent(cx);
}

fn goto_child(picker: &mut Picker<PathBuf>, cx: &mut Context) {
picker.goto_child(cx);
}
}

pub type MenuCallback<T> = Box<dyn Fn(&mut Editor, Option<&T>, MenuEvent)>;
Expand Down Expand Up @@ -251,7 +264,7 @@ impl<T: Item + PartialEq> Menu<T> {
}
}

use super::PromptEvent as MenuEvent;
use super::{Picker, PromptEvent as MenuEvent};

impl<T: Item + 'static> Component for Menu<T> {
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
Expand Down
Loading