feat: toggle diff explorer and show file change indicators
This commit is contained in:
+47
-5
@@ -38,6 +38,8 @@ pub struct FileRow<'a> {
|
||||
/// The actual project-relative OS path, never reconstructed from the label.
|
||||
pub path: &'a Path,
|
||||
pub label: &'a str,
|
||||
/// Original virtual-catalog input index; absent for scanned entries and directories.
|
||||
pub catalog_index: Option<usize>,
|
||||
pub is_dir: bool,
|
||||
pub depth: usize,
|
||||
pub expanded: bool,
|
||||
@@ -48,6 +50,7 @@ struct Node {
|
||||
path: PathBuf,
|
||||
label: String,
|
||||
search_label: String,
|
||||
catalog_index: Option<usize>,
|
||||
is_dir: bool,
|
||||
parent: Option<usize>,
|
||||
children: Vec<usize>,
|
||||
@@ -153,6 +156,7 @@ impl FilePicker {
|
||||
path,
|
||||
search_label: label.to_lowercase(),
|
||||
label,
|
||||
catalog_index: None,
|
||||
is_dir: entry.file_type().unwrap().is_dir(),
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
@@ -163,25 +167,26 @@ impl FilePicker {
|
||||
|
||||
/// Builds the changed-path catalog without consulting the working tree.
|
||||
pub fn from_paths(root: PathBuf, paths: impl IntoIterator<Item = PathBuf>) -> Self {
|
||||
let mut catalog = HashSet::new();
|
||||
for path in paths {
|
||||
let mut catalog = HashMap::new();
|
||||
for (index, path) in paths.into_iter().enumerate() {
|
||||
for parent in path
|
||||
.ancestors()
|
||||
.skip(1)
|
||||
.filter(|path| !path.as_os_str().is_empty())
|
||||
{
|
||||
catalog.insert((parent.to_owned(), true));
|
||||
catalog.insert((parent.to_owned(), true), None);
|
||||
}
|
||||
catalog.insert((path, false));
|
||||
catalog.entry((path, false)).or_insert(Some(index));
|
||||
}
|
||||
let nodes = catalog
|
||||
.into_iter()
|
||||
.map(|(path, is_dir)| {
|
||||
.map(|((path, is_dir), catalog_index)| {
|
||||
let label = label(&path);
|
||||
Node {
|
||||
path,
|
||||
search_label: label.to_lowercase(),
|
||||
label,
|
||||
catalog_index,
|
||||
is_dir,
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
@@ -310,6 +315,7 @@ impl FilePicker {
|
||||
.tree
|
||||
.map_or(node.label.as_str(), |tree| self.tree[tree].label.as_str()),
|
||||
is_dir: node.is_dir,
|
||||
catalog_index: node.catalog_index,
|
||||
depth: row.depth,
|
||||
expanded: row.expanded,
|
||||
}
|
||||
@@ -797,6 +803,30 @@ mod tests {
|
||||
picker.reveal_path(Path::new("src/deep/deleted.rs"));
|
||||
assert_eq!(picker.accept(), Some(PathBuf::from("src/deep/deleted.rs")));
|
||||
assert_eq!(picker.entries().filter(|row| !row.is_dir).count(), 3);
|
||||
let expected_index = |row: &FileRow<'_>| {
|
||||
if row.is_dir {
|
||||
None
|
||||
} else {
|
||||
Some(match row.path.to_str().unwrap() {
|
||||
"x" => 0,
|
||||
"x/y" => 1,
|
||||
"src/deep/deleted.rs" => 2,
|
||||
path => panic!("unexpected path {path}"),
|
||||
})
|
||||
}
|
||||
};
|
||||
for view in [FileView::List, FileView::Tree] {
|
||||
picker.set_view(view);
|
||||
for query in ["", "x", "deleted"] {
|
||||
picker.query = query.into();
|
||||
picker.refilter();
|
||||
let rows: Vec<_> = picker.entries().collect();
|
||||
assert!(!rows.is_empty());
|
||||
for row in rows {
|
||||
assert_eq!(row.catalog_index, expected_index(&row));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
@@ -831,6 +861,18 @@ mod tests {
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scanned_files_and_directories_have_no_catalog_index() {
|
||||
let directory = Directory::new();
|
||||
directory.put("nested/file.txt", "");
|
||||
let mut picker = directory.scan();
|
||||
for view in [FileView::List, FileView::Tree] {
|
||||
picker.set_view(view);
|
||||
picker.reveal_path(Path::new("nested/file.txt"));
|
||||
assert!(picker.entries().all(|row| row.catalog_index.is_none()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_ignore_negation_hidden_files_and_vcs_metadata() {
|
||||
let directory = Directory::new();
|
||||
|
||||
Reference in New Issue
Block a user