Show path in file picker and explorer

The file picker and explorer can each be opened in several ways, each
causing it to have a different root path. What's more, the file explorer
can change its path while you have it open.

This can make it very difficult to keep track of the root directory for
these pickers, so we add it as a header.

If desired, we could alternatively make this a configuration option, but
I see little downside to always including it.
pull/12806/head
Paho Lurie-Gregg 2025-02-07 01:52:41 -08:00
parent 0815b52e09
commit 9dc6fb8216
2 changed files with 30 additions and 3 deletions

View File

@ -32,6 +32,7 @@ pub use text::Text;
use helix_view::Editor;
use tui::text::{Span, Spans};
use std::borrow::Cow;
use std::path::Path;
use std::{error::Error, path::PathBuf};
@ -185,6 +186,23 @@ pub fn raw_regex_prompt(
cx.push_layer(Box::new(prompt));
}
/// Get the relative directory as a string.
///
/// NOTE: Assumes the given path is a directory, and will always output wiht a
/// trailing slash.
fn get_relative_dir(path: &Path) -> Cow<'static, str> {
let path = helix_stdx::path::get_relative_path(path);
if path.components().next().is_none() {
"./".into()
} else {
let mut str = path.to_string_lossy().into_owned();
if !str.ends_with('/') {
str.push('/');
}
str.into()
}
}
#[derive(Debug)]
pub struct FilePickerData {
root: PathBuf,
@ -246,7 +264,7 @@ pub fn file_picker(editor: &Editor, root: PathBuf) -> FilePicker {
log::debug!("file_picker init {:?}", Instant::now().duration_since(now));
let columns = [PickerColumn::new(
"path",
get_relative_dir(&root),
|item: &PathBuf, data: &FilePickerData| {
let path = item.strip_prefix(&data.root).unwrap_or(item);
let mut spans = Vec::with_capacity(3);
@ -274,6 +292,7 @@ pub fn file_picker(editor: &Editor, root: PathBuf) -> FilePicker {
cx.editor.set_error(err);
}
})
.always_show_headers()
.with_preview(|_editor, path| Some((path.as_path().into(), None)));
let injector = picker.injector();
let timeout = std::time::Instant::now() + std::time::Duration::from_millis(30);
@ -307,7 +326,7 @@ pub fn file_explorer(root: PathBuf, editor: &Editor) -> Result<FileExplorer, std
let directory_content = directory_content(&root)?;
let columns = [PickerColumn::new(
"path",
get_relative_dir(&root),
|(path, is_dir): &(PathBuf, bool), (root, directory_style): &(PathBuf, Style)| {
let name = path.strip_prefix(root).unwrap_or(path).to_string_lossy();
if *is_dir {
@ -345,6 +364,7 @@ pub fn file_explorer(root: PathBuf, editor: &Editor) -> Result<FileExplorer, std
}
},
)
.always_show_headers()
.with_preview(|_editor, (path, _is_dir)| Some((path.as_path().into(), None)));
Ok(picker)

View File

@ -241,6 +241,7 @@ type DynQueryCallback<T, D> =
pub struct Picker<T: 'static + Send + Sync, D: 'static> {
columns: Arc<[Column<T, D>]>,
primary_column: usize,
always_show_headers: bool,
editor_data: Arc<D>,
version: Arc<AtomicUsize>,
matcher: Nucleo<T>,
@ -373,6 +374,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
Self {
columns,
primary_column: default_column,
always_show_headers: false,
matcher,
editor_data,
version,
@ -424,6 +426,11 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
self
}
pub fn always_show_headers(mut self) -> Self {
self.always_show_headers = true;
self
}
pub fn with_dynamic_query(
mut self,
callback: DynQueryCallback<T, D>,
@ -818,7 +825,7 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
.widths(&self.widths);
// -- Header
if self.columns.len() > 1 {
if self.always_show_headers || self.columns.len() > 1 {
let active_column = self.query.active_column(self.prompt.position());
let header_style = cx.editor.theme.get("ui.picker.header");
let header_column_style = cx.editor.theme.get("ui.picker.header.column");