T13: the right column's Git and Files switch, kept in settings; Files shows the shown session's folder as a tree, folders first, Git's marks on names from git status without optional locks, unreadable folders noted; a double press opens a folder, or a file in the system's app; it reloads when the session reports a folder, so at each prompt; a program without shell integration gets a line saying it reports no folder; header controls keep their presses from the header's double-press zoom
This commit is contained in:
11 files changed
+700
-27
No files matched your search
@@ -0,0 +1,135 @@
|
||||
use std::env;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A fresh folder under the temp dir.
|
||||
fn folder(name: &str) -> PathBuf {
|
||||
let folder = env::temp_dir().join(format!("elyterminal-{name}-{}", std::process::id()));
|
||||
if folder.exists() {
|
||||
fs::remove_dir_all(&folder).expect("a stale test folder goes");
|
||||
}
|
||||
fs::create_dir_all(&folder).expect("a test folder");
|
||||
folder
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn folders_come_first_then_files_by_name_ignoring_case() {
|
||||
let root = folder("files-list");
|
||||
for name in ["Zeta", "alpha"] {
|
||||
fs::create_dir(root.join(name)).expect("a folder");
|
||||
}
|
||||
for name in ["c.txt", "B.txt", "a.txt"] {
|
||||
fs::write(root.join(name), "").expect("a file");
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::symlink;
|
||||
symlink(root.join("Zeta"), root.join("link")).expect("a link to a folder");
|
||||
symlink(root.join("nowhere"), root.join("broken")).expect("a broken link");
|
||||
}
|
||||
let expected: &[(&str, bool)] = if cfg!(unix) {
|
||||
&[
|
||||
("alpha", true),
|
||||
("link", true),
|
||||
("Zeta", true),
|
||||
("a.txt", false),
|
||||
("B.txt", false),
|
||||
("broken", false),
|
||||
("c.txt", false),
|
||||
]
|
||||
} else {
|
||||
&[
|
||||
("alpha", true),
|
||||
("Zeta", true),
|
||||
("a.txt", false),
|
||||
("B.txt", false),
|
||||
("c.txt", false),
|
||||
]
|
||||
};
|
||||
let listed: Vec<_> = list(&root).expect("a listing");
|
||||
let listed: Vec<_> = listed
|
||||
.iter()
|
||||
.map(|entry| (entry.name.as_str(), entry.folder))
|
||||
.collect();
|
||||
assert_eq!(listed, expected);
|
||||
assert!(list(&root.join("missing")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_mark_dots_each_folder_above_it_up_to_the_root() {
|
||||
let root = Path::new("/r");
|
||||
let changes = vec![
|
||||
(
|
||||
root.join("src").join("ui").join("a.rs"),
|
||||
GitStatus::Modified,
|
||||
),
|
||||
(root.join("gone.txt"), GitStatus::Deleted),
|
||||
];
|
||||
let (marks, dirty) = marks(root, changes);
|
||||
assert_eq!(marks.len(), 2);
|
||||
let expected = HashSet::from([
|
||||
root.to_path_buf(),
|
||||
root.join("src"),
|
||||
root.join("src").join("ui"),
|
||||
]);
|
||||
assert_eq!(dirty, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_tree_wears_marks_and_an_untracked_folder_marks_what_it_holds() {
|
||||
let root = PathBuf::from("/r");
|
||||
let (notes, src, locked) = (root.join("notes"), root.join("src"), root.join("locked"));
|
||||
let entry = |name: &str, folder| Entry {
|
||||
name: name.into(),
|
||||
folder,
|
||||
};
|
||||
let mut files = Files::new(root.clone());
|
||||
let top = vec![
|
||||
entry("notes", true),
|
||||
entry("src", true),
|
||||
entry("locked", true),
|
||||
entry("a.txt", false),
|
||||
];
|
||||
files.listings.insert(root.clone(), Ok(top));
|
||||
let inside = vec![entry("deep", true), entry("todo.txt", false)];
|
||||
files.listings.insert(notes.clone(), Ok(inside));
|
||||
files
|
||||
.listings
|
||||
.insert(src.clone(), Ok(vec![entry("main.rs", false)]));
|
||||
files.listings.insert(locked.clone(), Err("denied".into()));
|
||||
let changes = vec![
|
||||
(notes.clone(), GitStatus::Untracked),
|
||||
(src.join("main.rs"), GitStatus::Modified),
|
||||
];
|
||||
(files.marks, files.dirty) = marks(&root, changes);
|
||||
|
||||
let node = |path: &Path, icon| {
|
||||
let label = path
|
||||
.file_name()
|
||||
.expect("a name")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
TreeNode::new(key(path), label).icon(icon)
|
||||
};
|
||||
let untracked = |node: TreeNode| node.note("U").tone(Tone::Success);
|
||||
let dotted = |node: TreeNode| node.note("•").tone(Tone::Warning);
|
||||
let expected = dotted(
|
||||
node(&root, IconName::Folder).children([
|
||||
untracked(node(¬es, IconName::Folder).children([
|
||||
untracked(node(¬es.join("deep"), IconName::Folder).pending()),
|
||||
untracked(node(¬es.join("todo.txt"), IconName::File)),
|
||||
])),
|
||||
dotted(
|
||||
node(&src, IconName::Folder).children([node(&src.join("main.rs"), IconName::File)
|
||||
.note("M")
|
||||
.tone(Tone::Warning)]),
|
||||
),
|
||||
node(&locked, IconName::Folder)
|
||||
.children([])
|
||||
.note("unreadable")
|
||||
.tone(Tone::Danger),
|
||||
node(&root.join("a.txt"), IconName::File),
|
||||
]),
|
||||
);
|
||||
assert_eq!(files.folder(&root, "r".into(), false), expected);
|
||||
}
|
||||
Reference in new issue
Block a user