refactor: pass EditorData to callbacks, do not "compute" the root

pull/12902/head
Nikita Revenco 2025-02-18 16:49:00 +00:00
parent 67ca955baa
commit 984ad4bca9
2 changed files with 207 additions and 207 deletions

View File

@ -35,6 +35,7 @@ use tui::text::Span;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use std::{error::Error, path::PathBuf}; use std::{error::Error, path::PathBuf};
use self::picker::PickerKeyHandler; use self::picker::PickerKeyHandler;
@ -283,7 +284,14 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
picker picker
} }
type FileExplorer = Picker<(PathBuf, bool), (PathBuf, Style)>; /// for each path: (path to item, is the path a directory?)
type ExplorerItem = (PathBuf, bool);
/// (file explorer root, directory style)
type ExplorerData = (PathBuf, Style);
type FileExplorer = Picker<ExplorerItem, ExplorerData>;
type KeyHandler = PickerKeyHandler<ExplorerItem, ExplorerData>;
type OnConfirm = fn( type OnConfirm = fn(
cursor: u32, cursor: u32,
@ -334,13 +342,14 @@ fn create_confirmation_prompt(
cx.jobs.callback(callback); cx.jobs.callback(callback);
} }
type FileOperation = fn(u32, &mut Context, &Path, &str) -> Option<Result<String, String>>; type FileOperation = fn(PathBuf, u32, &mut Context, &Path, &str) -> Option<Result<String, String>>;
fn create_file_operation_prompt( fn create_file_operation_prompt(
cursor: u32, cursor: u32,
prompt: &'static str, prompt: &'static str,
cx: &mut Context, cx: &mut Context,
path: &Path, path: &Path,
data: Arc<ExplorerData>,
compute_initial_line: fn(&Path) -> String, compute_initial_line: fn(&Path) -> String,
file_op: FileOperation, file_op: FileOperation,
) { ) {
@ -359,7 +368,7 @@ fn create_file_operation_prompt(
let path = cx.editor.file_explorer_selected_path.clone(); let path = cx.editor.file_explorer_selected_path.clone();
if let Some(path) = path { if let Some(path) = path {
match file_op(cursor, cx, &path, input) { match file_op(data.0.clone(), cursor, cx, &path, input) {
Some(Ok(msg)) => cx.editor.set_status(msg), Some(Ok(msg)) => cx.editor.set_status(msg),
Some(Err(msg)) => cx.editor.set_error(msg), Some(Err(msg)) => cx.editor.set_error(msg),
None => (), None => (),
@ -396,16 +405,6 @@ fn refresh_file_explorer(cursor: u32, cx: &mut Context, root: PathBuf) {
cx.jobs.callback(callback); cx.jobs.callback(callback);
} }
/// We don't have access to the file explorer's current directory directly,
/// but we can get it by taking any of the children of the explorer
/// and obtaining their parents.
fn root_from_child(child: &Path) -> PathBuf {
child
.parent()
.map(|p| p.to_path_buf())
.unwrap_or(helix_stdx::env::current_working_dir())
}
pub fn file_explorer( pub fn file_explorer(
cursor: Option<u32>, cursor: Option<u32>,
root: PathBuf, root: PathBuf,
@ -426,7 +425,8 @@ pub fn file_explorer(
}, },
)]; )];
let yank_path = |cx: &mut Context, (path, _is_dir): &(PathBuf, bool), _cursor: u32| { let yank_path =
|cx: &mut Context, (path, _is_dir): &ExplorerItem, _: Arc<ExplorerData>, _cursor: u32| {
let register = cx let register = cx
.editor .editor
.selected_register .selected_register
@ -441,18 +441,20 @@ pub fn file_explorer(
}; };
}; };
let create_file = |cx: &mut Context, (path, _is_dir): &(PathBuf, bool), cursor: u32| { let create_file =
|cx: &mut Context, (path, _is_dir): &ExplorerItem, data: Arc<ExplorerData>, cursor: u32| {
create_file_operation_prompt( create_file_operation_prompt(
cursor, cursor,
"create:", "create:",
cx, cx,
path, path,
data,
|path| { |path| {
path.parent() path.parent()
.map(|p| format!("{}{}", p.display(), std::path::MAIN_SEPARATOR)) .map(|p| format!("{}{}", p.display(), std::path::MAIN_SEPARATOR))
.unwrap_or_default() .unwrap_or_default()
}, },
|cursor, cx, path, to_create_str| { |root, cursor, cx, _, to_create_str| {
let to_create = helix_stdx::path::expand_tilde(PathBuf::from(to_create_str)); let to_create = helix_stdx::path::expand_tilde(PathBuf::from(to_create_str));
let create_op = |cursor: u32, let create_op = |cursor: u32,
@ -481,8 +483,6 @@ pub fn file_explorer(
} }
}; };
let root = root_from_child(path);
if to_create.exists() { if to_create.exists() {
create_confirmation_prompt( create_confirmation_prompt(
cursor, cursor,
@ -504,14 +504,16 @@ pub fn file_explorer(
) )
}; };
let move_file = |cx: &mut Context, (path, _is_dir): &(PathBuf, bool), cursor: u32| { let move_file =
|cx: &mut Context, (path, _is_dir): &ExplorerItem, data: Arc<ExplorerData>, cursor: u32| {
create_file_operation_prompt( create_file_operation_prompt(
cursor, cursor,
"move:", "move:",
cx, cx,
path, path,
data,
|path| path.display().to_string(), |path| path.display().to_string(),
|cursor, cx, move_from, move_to_str| { |root, cursor, cx, move_from, move_to_str| {
let move_to = helix_stdx::path::expand_tilde(PathBuf::from(move_to_str)); let move_to = helix_stdx::path::expand_tilde(PathBuf::from(move_to_str));
let move_op = |cursor: u32, let move_op = |cursor: u32,
@ -538,8 +540,6 @@ pub fn file_explorer(
None None
}; };
let root = root_from_child(move_from);
if move_to.exists() { if move_to.exists() {
create_confirmation_prompt( create_confirmation_prompt(
cursor, cursor,
@ -561,21 +561,23 @@ pub fn file_explorer(
) )
}; };
let delete_file = |cx: &mut Context, (path, _is_dir): &(PathBuf, bool), cursor: u32| { let delete_file = |cx: &mut Context,
(path, _is_dir): &ExplorerItem,
data: Arc<ExplorerData>,
cursor: u32| {
create_file_operation_prompt( create_file_operation_prompt(
cursor, cursor,
"delete? (y/n):", "delete? (y/n):",
cx, cx,
path, path,
data,
|_| "".to_string(), |_| "".to_string(),
|cursor, cx, to_delete, confirmation| { |root, cursor, cx, to_delete, confirmation| {
if confirmation == "y" { if confirmation == "y" {
if !to_delete.exists() { if !to_delete.exists() {
return Some(Err(format!("Path {} does not exist", to_delete.display()))); return Some(Err(format!("Path {} does not exist", to_delete.display())));
}; };
let root = root_from_child(to_delete);
if confirmation.ends_with(std::path::MAIN_SEPARATOR) { if confirmation.ends_with(std::path::MAIN_SEPARATOR) {
if let Err(err) = fs::remove_dir_all(to_delete).map_err(|err| { if let Err(err) = fs::remove_dir_all(to_delete).map_err(|err| {
format!("Unable to delete directory {}: {err}", to_delete.display()) format!("Unable to delete directory {}: {err}", to_delete.display())
@ -602,18 +604,20 @@ pub fn file_explorer(
) )
}; };
let copy_file = |cx: &mut Context, (path, _is_dir): &(PathBuf, bool), cursor: u32| { let copy_file =
|cx: &mut Context, (path, _is_dir): &ExplorerItem, data: Arc<ExplorerData>, cursor: u32| {
create_file_operation_prompt( create_file_operation_prompt(
cursor, cursor,
"copy-to:", "copy-to:",
cx, cx,
path, path,
data,
|path| { |path| {
path.parent() path.parent()
.map(|p| format!("{}{}", p.display(), std::path::MAIN_SEPARATOR)) .map(|p| format!("{}{}", p.display(), std::path::MAIN_SEPARATOR))
.unwrap_or_default() .unwrap_or_default()
}, },
|cursor, cx, copy_from, copy_to_str| { |root, cursor, cx, copy_from, copy_to_str| {
let copy_to = helix_stdx::path::expand_tilde(PathBuf::from(copy_to_str)); let copy_to = helix_stdx::path::expand_tilde(PathBuf::from(copy_to_str));
let copy_op = |cursor: u32, let copy_op = |cursor: u32,
@ -640,8 +644,6 @@ pub fn file_explorer(
))) )))
}; };
let root = root_from_child(&copy_to);
if copy_from.is_dir() || copy_to_str.ends_with(std::path::MAIN_SEPARATOR) { if copy_from.is_dir() || copy_to_str.ends_with(std::path::MAIN_SEPARATOR) {
// TODO: support copying directories (recursively)?. This isn't built-in to the standard library // TODO: support copying directories (recursively)?. This isn't built-in to the standard library
Some(Err(format!( Some(Err(format!(
@ -669,14 +671,12 @@ pub fn file_explorer(
) )
}; };
type KeyHandler = PickerKeyHandler<(PathBuf, bool)>;
let picker = Picker::new( let picker = Picker::new(
columns, columns,
0, 0,
directory_content, directory_content,
(root, directory_style), (root, directory_style),
move |cx, (path, is_dir): &(PathBuf, bool), action| { move |cx, (path, is_dir): &ExplorerItem, action| {
if *is_dir { if *is_dir {
let new_root = helix_stdx::path::normalize(path); let new_root = helix_stdx::path::normalize(path);
let callback = Box::pin(async move { let callback = Box::pin(async move {

View File

@ -259,7 +259,7 @@ pub struct Picker<T: 'static + Send + Sync, D: 'static> {
widths: Vec<Constraint>, widths: Vec<Constraint>,
callback_fn: PickerCallback<T>, callback_fn: PickerCallback<T>,
custom_key_handlers: PickerKeyHandlers<T>, custom_key_handlers: PickerKeyHandlers<T, D>,
pub truncate_start: bool, pub truncate_start: bool,
/// Caches paths to documents /// Caches paths to documents
@ -395,7 +395,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
} }
} }
pub fn with_key_handlers(mut self, handlers: PickerKeyHandlers<T>) -> Self { pub fn with_key_handlers(mut self, handlers: PickerKeyHandlers<T, D>) -> Self {
self.custom_key_handlers = handlers; self.custom_key_handlers = handlers;
self self
} }
@ -526,7 +526,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
if let (Some(callback), Some(selected)) = if let (Some(callback), Some(selected)) =
(self.custom_key_handlers.get(event), self.selection()) (self.custom_key_handlers.get(event), self.selection())
{ {
callback(cx, selected, self.cursor); callback(cx, selected, Arc::clone(&self.editor_data), self.cursor);
EventResult::Consumed(None) EventResult::Consumed(None)
} else { } else {
EventResult::Ignored(None) EventResult::Ignored(None)
@ -1185,5 +1185,5 @@ impl<T: 'static + Send + Sync, D> Drop for Picker<T, D> {
} }
type PickerCallback<T> = Box<dyn Fn(&mut Context, &T, Action)>; type PickerCallback<T> = Box<dyn Fn(&mut Context, &T, Action)>;
pub type PickerKeyHandler<T> = Box<dyn Fn(&mut Context, &T, u32) + 'static>; pub type PickerKeyHandler<T, D> = Box<dyn Fn(&mut Context, &T, Arc<D>, u32) + 'static>;
pub type PickerKeyHandlers<T> = HashMap<KeyEvent, PickerKeyHandler<T>>; pub type PickerKeyHandlers<T, D> = HashMap<KeyEvent, PickerKeyHandler<T, D>>;